最近,有群里在群里发了这么一个非常有意思的卡片 Hover 动效,来源于此网站 -- key-drop,效果如下:

非常有意思酷炫的效果。而本文,我们不会完全还原此效果,而是基于此效果,尝试去制作这么一个类似的卡片交互效果:

该效果的几个核心点:

  1. 卡片的 3D 旋转跟随鼠标移动效果
  2. 如何让卡片在 Hover 状态,有不同的光泽变化
  3. 如何让卡片在 Hover 状态,有 Blink,Blink 的星星闪烁效果

当然,要做到卡片的 3D 旋转跟随鼠标移动效果需要一定程度的借助 JavaScript,因此,最终的效果是 CSS 配合 JavaScript 以及一些动态效果的 Gif 共同实现。

好,下面就让我们一步一步一起来实现这个效果。

卡片的 3D 旋转跟随效果

OK,接下来,如何实现 3D 卡片效果呢?

这个效果之前在 让交互更加生动!有意思的鼠标跟随 3D 旋转动效 实现过一次,我们复习一下。

这个交互效果主要有两个核心:

  1. 借助了 CSS 3D 的能力
  2. 元素的旋转需要和鼠标的移动相结合

我们的目标是实现这样一个动画效果:

这里,我们其实有两个核心元素:

  1. 鼠标活动区域
  2. 旋转物体本身

鼠标在鼠标活动区域内的移动,会影响旋转物体本身的 3D 旋转,而旋转的方向其实可以被分解为 X 轴方向与 Y 轴方向。

我们来看一下,假设我们的 HTML 结构如下:

  1. <body>
  2. <div id="element"></div>
  3. </body>

得到这样一个图形:

这里,body 的范围就是整个鼠标可活动区域,也是我们添加鼠标的 mousemove 事件的宿主 target,而 #element 就是需要跟随鼠标一起转动的旋转物体本身。

因为整个效果是需要基于 CSS 3D 的,我们首先加上简单的 CSS 3D 效果:

  1. body {
  2. width: 100vw;
  3. height: 100vh;
  4. transform-style: preserve-3d;
  5. perspective: 500px;
  6. }
  7. div {
  8. width: 200px;
  9. height: 200px;
  10. background: #000;
  11. transform-style: preserve-3d;
  12. }

效果如下:

没有什么不一样。这是因为还没有添加任何的 3D 变换,我们给元素添加 X、Y 两个方向的 rotate() 试一下(注意,这里默认的旋转圆心即是元素中心):

  1. div {
  2. transform: rotateX(15deg) rotateY(30deg);
  3. }

效果如下,是有那么点意思了:

好,接下来,我们的目标就是通过结合 mouseover 事件,让元素动起来。

控制 X 方向的移动

当然,为了更加容易理解,我们把动画拆分为 X、Y 两个方向上的移动。首先看 X 方向上的移动:

这里,我们需要以元素的中心为界:

  1. 当鼠标在中心右侧连续移动,元素绕 Y 轴移动,并且值从 0 开始,越来越大,范围为(0, +∞)deg
  2. 反之,当鼠标在中心左侧连续移动,元素绕 Y 轴移动,并且值从 0 开始,越来越小,范围为(-∞, 0)deg

这样,我们可以得到这样一个公式:

rotateY = (鼠标 x 坐标 - 元素左上角 x 坐标 - 元素宽度的一半)deg

通过绑定 onmousemove 事件,我们尝试一下:

  1. const mouseOverContainer = document.getElementsByTagName("body")[0];
  2. const element = document.getElementById("element");
  3. mouseOverContainer.onmousemove = function(e) {
  4. let box = element.getBoundingClientRect();
  5. let calcY = e.clientX - box.x - (box.width / 2);
  6. element.style.transform = "rotateY(" + calcY + "deg) ";
  7. }

效果如下:

好吧,旋转的太夸张了,因此,我们需要加一个倍数进行控制:

  1. const multiple = 20;
  2. const mouseOverContainer = document.getElementsByTagName("body")[0];
  3. const element = document.getElementById("element");
  4. mouseOverContainer.onmousemove = function(e) {
  5. let box = element.getBoundingClientRect();
  6. let calcY = (e.clientX - box.x - (box.width / 2)) / multiple;
  7. element.style.transform = "rotateY(" + calcY + "deg) ";
  8. }

通过一个倍数约束后,效果好了不少:

控制 Y 方向的移动

同理,我们利用上述的方式,同样可以控制 Y 方向上的移动:

  1. const multiple = 20;
  2. const mouseOverContainer = document.getElementsByTagName("body")[0];
  3. const element = document.getElementById("element");
  4. mouseOverContainer.onmousemove = function(e) {
  5. let box = element.getBoundingClientRect();
  6. let calcX = (e.clientY - box.y - (box.height / 2)) / multiple;
  7. element.style.transform = "rotateX(" + calcX + "deg) ";
  8. };

效果如下:

当然,在这里,我们会发现方向是元素运动的方向是反的,所以需要做一下取反处理,修改下 calcX 的值,乘以一个 -1 即可:

  1. let calcX = (e.clientY - box.y - (box.height / 2)) / multiple * -1;

结合 X、Y 方向的移动

OK,到这里,我们只需要把上述的结果合并一下即可,同时,上面我们使用的是 onmousemove 触发每一次动画移动。现代 Web 动画中,我们更倾向于使用 requestAnimationFrame 去优化我们的动画,确保每一帧渲染一次动画即可。

完整的改造后的代码如下:

  1. const multiple = 20;
  2. const mouseOverContainer = document.getElementsByTagName("body")[0];
  3. const element = document.getElementById("element");
  4. function transformElement(x, y) {
  5. let box = element.getBoundingClientRect();
  6. let calcX = -(y - box.y - (box.height / 2)) / multiple;
  7. let calcY = (x - box.x - (box.width / 2)) / multiple;
  8. element.style.transform = "rotateX("+ calcX +"deg) "
  9. + "rotateY("+ calcY +"deg)";
  10. }
  11. mouseOverContainer.addEventListener('mousemove', (e) => {
  12. window.requestAnimationFrame(function(){
  13. transformElement(e.clientX, e.clientY);
  14. });
  15. });

至此,我们就能简单的实现题图所示的鼠标跟随 3D 旋转动效:

设置平滑出入

现在,还有最后一个问题,就是当我们的鼠标离开活动区域时,元素的 transform 将停留在最后一帧,正确的表现应该是复原到原状。因此,我们还需要添加一些事件监听做到元素的平滑复位。

通过一个 mouseleave 事件配合元素的 transition 即可。

  1. div {
  2. // 与上述保持一致...
  3. transition: all .2s;
  4. }
  1. mouseOverContainer.addEventListener('mouseleave', (e) => {
  2. window.requestAnimationFrame(function(){
  3. element.style.transform = "rotateX(0) rotateY(0)";
  4. });
  5. });

至此,我们就可以完美的实现平滑出入,整体效果最终如下:

完整的代码,你可以戳这里:CodePen Demo -- CSS 3D Rotate With Mouse Move

Hover 状态下的光泽变化

好,有了上述铺垫之后,我们就可以将黑色背景图,替换成实际的图片,得到这么一个初步效果:

接下来,我们需要让卡片能够变得有光泽,并且也能基于鼠标 Hover 的坐标不同,展现出不一样的效果,像是这样:

怎么实现呢?看似复杂,其实只需要简单的利用混合模式即可。其中本质就是图片叠加上黑白相间的渐变,再调整混合模式,就能实现上述的高光效果。

代码如下:

  1. <div></div>
  1. div {
  2. position: relative;
  3. background: url('image.png');
  4. &::before {
  5. content: "";
  6. position: absolute;
  7. inset: 0;
  8. background:
  9. linear-gradient(
  10. 115deg,
  11. transparent 0%,
  12. rgba(255, 255, 255, 0.5 30%),
  13. rgba(0, 0, 0, .5) 55%),
  14. rgba(255, 255, 255, .5) 80%),
  15. transparent 100%
  16. );
  17. mix-blend-mode: color-dodge;
  18. }
  19. }

这里,我们利用 div 元素的背景展示了图片,利用元素的伪元素展示了黑白渐变效果,最终再叠加上混合模式 mix-blend-mode: color-dodge,示意图如下:

但是,此时,只有卡片是有 3D 效果的,叠加的黑白渐变层是不会随着 Hover 效果进行变化的:

为了解决这个问题,我们需要让渐变图层也能受到 Hover 的动态影响,这个好做,我们额外引入一个 CSS 变量,基于鼠标当前 Hover 卡片时,距离卡片最左侧的横向距离,设置动态的 CSS 变量。

改造一下代码:

  1. <div id="g-img"></div>
  1. div {
  2. --per: 30%;
  3. position: relative;
  4. // ...
  5. &::before {
  6. content: "";
  7. position: absolute;
  8. inset: 0;
  9. background:
  10. linear-gradient(
  11. 115deg,
  12. transparent 0%,
  13. rgba(255, 255, 255, 0.5) var(--per),
  14. rgba(0, 0, 0, .5) calc(var(--per) + 25%),
  15. rgba(255, 255, 255, .5) calc(var(--per) + 50%),
  16. transparent 100%
  17. );
  18. mix-blend-mode: color-dodge;
  19. }
  20. }
  1. const multiple = 15;
  2. const mouseOverContainer = document.getElementsByTagName("body")[0];
  3. const element = document.getElementById("element");
  4. const img = document.getElementById("g-img");
  5. function transformElement(x, y) {
  6. let box = element.getBoundingClientRect();
  7. const calcX = -(y - box.y - box.height / 2) / multiple;
  8. const calcY = (x - box.x - box.width / 2) / multiple;
  9. const percentage = parseInt((x - box.x) / box.width * 1000) / 10;
  10. element.style.transform = "rotateX(" + calcX + "deg) " + "rotateY(" + calcY + "deg)";
  11. // 额外增加一个控制 --per 的变量写入
  12. img.style = `--per: ${percentage}%`;
  13. }
  14. mouseOverContainer.addEventListener("mousemove", (e) => {
  15. window.requestAnimationFrame(function () {
  16. transformElement(e.clientX, e.clientY);
  17. });
  18. });

简单解释一下,上述代码最核心的部分就是引入了 --per CSS 变量,其应用在渐变代码中。

我们通过计算当前鼠标距离卡片左侧的横向距离,除以卡片整体的宽度,得到 --per 实际表示的百分比,再赋值给 --per,以此实现 Hover 时候的光效变化:

叠加星星闪烁效果

好,效果已经非常接近了。当然,总感觉缺少什么,我们可以在这一步,继续叠加上另外一层星星闪烁的效果。

这里,我们可以用现成的 GIF 图,像是这样(图片来源于 Pokemon Card Holo Effect):

这样,我们的整个效果,其实就变成了这种叠加状态:

我们再简单改造一下代码:

  1. #g-img {
  2. --per: 30%;
  3. position: relative;
  4. background: url('image.png');
  5. &::after {
  6. content: "";
  7. display: none;
  8. position: absolute;
  9. inset: 0;
  10. background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/13471/sparkles.gif");
  11. mix-blend-mode: color-dodge;
  12. }
  13. &::before {
  14. content: "";
  15. display: none;
  16. position: absolute;
  17. background:
  18. linear-gradient(
  19. 115deg,
  20. transparent 0%,
  21. rgba(255, 255, 255, 0.7) var(--per),
  22. rgba(0, 0, 0, .6) calc(var(--per) + 25%),
  23. rgba(255, 255, 255, .5) calc(var(--per) + 50%),
  24. transparent 100%
  25. );
  26. mix-blend-mode: color-dodge;
  27. }
  28. &:hover::after,
  29. &:hover::before {
  30. display: block;
  31. }
  32. }

当 Hover 状态下,才展示渐变背景与星星 Gif 图的叠加效果,最终,我们就实现了最开头的效果:

完整的代码,你可以戳这里 CodePen Demo -- CSS 3D Rotate With Mouse Move

尝试不同渐变背景与不同混合模式

了解上述制作方式的全过程后,我们就可以改变叠加的混合模式与渐变背景,以创造更多不一样的效果。

像是这样:

完整的代码,你可以戳这里 CodePen Demo -- CSS 3D Rotate With Mouse Move2

或者是这样:

完整的代码,你可以戳这里 CodePen Demo -- CSS 3D Rotate With Mouse Move3

最后

怎样,学会了吗。通过不同的混合模式与不同的渐变背景,可以排列组合出非常多种有趣有意思的效果。感兴趣的,一定动手试试!

好了,本文到此结束,希望本文对你有所帮助

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

【动画进阶】神奇的 3D 卡片反光闪烁动效的更多相关文章

  1. android动画特效之解决解决移动后闪烁现象,解决输入法弹出后位置回复原状,解决两个动画叠加

    以下代码实现的效果是: BoundOpenView从居中移动到顶部,移动完后,BoundSendView从隐藏变为显示,并从顶部移动BoundOpenView下方20dp处,同时透明度慢慢增加. pr ...

  2. iOS动画进阶 - 实现炫酷的上拉刷新动效

    移动端訪问不佳,请訪问我的个人博客 近期撸了一个上拉刷新的小轮子.仅仅要遵循一个协议就能自己定义自己动效的上拉刷新和载入,我自己也写了几个动效进去,以下是一个比較好的动效的实现过程 先上效果图和git ...

  3. 动效解析工厂:Mask 动画

    转载自:http://www.cocoachina.com/ios/20160214/15250.html 前言:很多动效都是多种动画的组合,有时候你可能只是需要其中某个动画,但面对庞杂的代码库或是教 ...

  4. HMS Core 3D流体仿真技术,打造移动端PC级流体动效

    移动设备硬件的高速发展,让游戏行业发生翻天覆地的变化,许多酷炫的游戏效果不再局限于电脑端,玩家在移动端就能享受到场景更逼真.画质更清晰.体验更流畅的游戏服务.但由于移动设备算力不足,为了实现真实感的水 ...

  5. 3D卡片折叠动画自定义下拉框

    在线演示 本地下载

  6. iOS 简单的动画自定义方法(旋转、移动、闪烁等)

    #define kDegreesToRadian(x) (M_PI * (x) / 180.0) #define kRadianToDegrees(radian) (radian*180.0)/(M_ ...

  7. CSS3动画与2D、3D转换

    一.过度动画:transition 五个属性: transition-property css 样式属性名称 transition-duration 动画持续时间(需要单位s) transition- ...

  8. 一组神奇的 3D Gif 动图

    本文由 极客范 - 黄利民 翻译自 mymodernmet.欢迎加入极客翻译小组,同我们一道翻译与分享.转载请参见文章末尾处的要求. 虽然 gif 动图/动画似乎是无处不在现在了,但有些聪明人已经把 ...

  9. CAReplicatorLayer复制Layer和动画, 实现神奇的效果

    今天我们看下CAReplicatorLayer, 官方的解释是一个高效处理复制图层的中间层.他能复制图层的所有属性,包括动画. 一样我们先看下头文件 @interface CAReplicatorLa ...

  10. iOS动画进阶 - 手摸手教你写ShineButton动画

    移动端访问不佳,请访问我的个人博客 前段时间在github上看见一个非常nice的动画效果,可惜是安卓的,想着用swift写一个iOS版的,下下来源代码研究了一下,下面是我写代码的心路历程 先上图和d ...

随机推荐

  1. DFS遍历图(链式邻接表实现)

    1 #include<iostream> 2 #include<string.h> 3 #include<stdlib.h> 4 #include<stdio ...

  2. 华企盾DSC邮件白名单问题常见处理方法

    1.先检查邮件白名单服务器配置测试连接的通(不通可能是协议未开或者账号密码错误) 2.检查邮件发送端口是否配置(常见的有25和s465.s587) 3.邮件是否到发件箱或者收件箱的垃圾邮件里面了 4. ...

  3. MinIO客户端之rb

    MinIO提供了一个命令行程序mc用于协助用户完成日常的维护.管理类工作. 官方资料 mc rb 彻底删除指定的桶. 命令如下: ./mc rb local1/bkt1 控制台的输出,如下: mc: ...

  4. ElasticSearch之Health API

    查看当前集群全部健康指标的信息,执行如下命令: curl -X GET "https://localhost:9200/_health_report?pretty" --cacer ...

  5. libGDX游戏开发之打包游戏(十二)

    libGDX游戏开发之打包游戏(十二) libGDX系列,游戏开发有unity3D巴拉巴拉的,为啥还用java开发?因为我是Java程序员emm-国内用libgdx比较少,多数情况需要去官网和goog ...

  6. 2023-09-13:用go语言,给定一个整数数组 nums 和一个正整数 k, 找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。 输入: nums = [4, 3, 2, 3, 5,

    2023-09-13:用go语言,给定一个整数数组 nums 和一个正整数 k, 找出是否有可能把这个数组分成 k 个非空子集,其总和都相等. 输入: nums = [4, 3, 2, 3, 5, 2 ...

  7. 基于AI的架构优化:创新数据集构造法提升Feature envy坏味道检测与重构准确率

    本文分享自华为云社区<华为云基于AI实现架构坏味道重构取得业界突破,相应文章已被软工顶会FSE 2023收录>,作者: 华为云软件分析Lab. 基于AI技术实现架构坏味道检测与重构建议是当 ...

  8. 【玩转鲲鹏DevKit系列】何如快速迁移有源码应用

    本文分享自华为云社区<[玩转鲲鹏DevKit系列]何如快速迁移有源码应用>,作者:华为云社区精选 . 源码(也称源程序)是程序员编写的计算机程序的文本形式,不同的编程语言有不同的语法和规则 ...

  9. 当你运行npm run命令时,会发生什么?

    摘要:今天我们来聊一聊运行npm run命令之后会发生什么. 本文分享自华为云社区<运行npm run命令的时候会发生什么?>,作者:gentle_zhou . 先前发了一篇"运 ...

  10. 联合枚举类型:从C语言看枚举与联合类型到TypeScript/Python

    枚举,还是从hello world 开奖,大部分的人应该是从C开始的,比如我.当然,这部分也可以跳过. 详说枚举类型: C语言中的enum 计算机入门时候有点印象: enum是C语言中的一个关键字,e ...