最近,有群里在群里发了这么一个非常有意思的卡片 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 结构如下:

<body>
<div id="element"></div>
</body>

得到这样一个图形:

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

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

body {
width: 100vw;
height: 100vh;
transform-style: preserve-3d;
perspective: 500px;
} div {
width: 200px;
height: 200px;
background: #000;
transform-style: preserve-3d;
}

效果如下:

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

div {
transform: rotateX(15deg) rotateY(30deg);
}

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

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

控制 X 方向的移动

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

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

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

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

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

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

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

效果如下:

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

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

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

控制 Y 方向的移动

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

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

效果如下:

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

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

结合 X、Y 方向的移动

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

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

const multiple = 20;
const mouseOverContainer = document.getElementsByTagName("body")[0];
const element = document.getElementById("element"); function transformElement(x, y) {
let box = element.getBoundingClientRect();
let calcX = -(y - box.y - (box.height / 2)) / multiple;
let calcY = (x - box.x - (box.width / 2)) / multiple; element.style.transform = "rotateX("+ calcX +"deg) "
+ "rotateY("+ calcY +"deg)";
} mouseOverContainer.addEventListener('mousemove', (e) => {
window.requestAnimationFrame(function(){
transformElement(e.clientX, e.clientY);
});
});

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

设置平滑出入

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

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

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

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

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

Hover 状态下的光泽变化

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

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

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

代码如下:

<div></div>
div {
position: relative;
background: url('image.png'); &::before {
content: "";
position: absolute;
inset: 0;
background:
linear-gradient(
115deg,
transparent 0%,
rgba(255, 255, 255, 0.5 30%),
rgba(0, 0, 0, .5) 55%),
rgba(255, 255, 255, .5) 80%),
transparent 100%
);
mix-blend-mode: color-dodge;
}
}

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

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

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

改造一下代码:

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

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

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

叠加星星闪烁效果

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

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

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

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

#g-img {
--per: 30%;
position: relative;
background: url('image.png'); &::after {
content: "";
display: none;
position: absolute;
inset: 0;
background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/13471/sparkles.gif");
mix-blend-mode: color-dodge;
} &::before {
content: "";
display: none;
position: absolute;
background:
linear-gradient(
115deg,
transparent 0%,
rgba(255, 255, 255, 0.7) var(--per),
rgba(0, 0, 0, .6) calc(var(--per) + 25%),
rgba(255, 255, 255, .5) calc(var(--per) + 50%),
transparent 100%
);
mix-blend-mode: color-dodge;
} &:hover::after,
&:hover::before {
display: block;
}
}

当 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. Go 泛型之类型参数

    Go 泛型之类型参数 一.Go 的泛型与其他主流编程语言的泛型差异 Go泛型和其他支持泛型的主流编程语言之间的泛型设计与实现存在差异一样,Go 的泛型与其他主流编程语言的泛型也是不同的.我们先看一下 ...

  2. 如何通过port-forward命令在本地访问 k8s 集群服务

    公众号「架构成长指南」,专注于生产实践.云原生.分布式系统.大数据技术分享 概述 在我们访问k8s中的pod服务时,一般通过node port映射pod端口进行访问,还有一种是通过ingress或者i ...

  3. JavaScript String对象及方法总结

    String 对象创建方法: new String() var txt1 = new String("string"); var txt2 = "string" ...

  4. Odoo接口说明

    通过使用PostMan调用Odoo - POST { "jsonrpc":"2.0", "params":{ "db": ...

  5. ElasticSearch之cat recovery API

    命令样例如下: curl -X GET "https://localhost:9200/_cat/recovery?v=true&pretty" --cacert $ES_ ...

  6. Ubuntu 安装MySQL 8.0.23及以上版本

    首先如果当前linux中没有wget,那么我们可以考虑使用sudo apt-get install wget来安装wget命令 Ubuntu自带的源只能安装MySQL5.7版本,这里去MySQL官网安 ...

  7. C语言基础之因子分解

    要求: 从键盘输入一个正整数,然后将该整数分解为1和各个质因子的相乘,如果输入的整数本身就是质数,则应分解为1和该数本身相乘. 输出格式: 因子分解,因子由小到大输出. 如:1* 2* 2* 3 代码 ...

  8. super只能用在构造方法里面吗?

    super关键字并不仅限于在构造方法中使用.在Java中,super关键字有两个主要的用途: 在子类的构造方法中调用父类的构造方法.这是super的一个常见用法,通常是在子类的构造方法的第一行使用,用 ...

  9. 文心一言 VS 讯飞星火 VS chatgpt (126)-- 算法导论11.1 1题

    一.用go语言,假设一动态集合 S用一个长度为 m 的直接寻址表T来表示.请给出一个查找 S 中最大元素的过程.你所给的过程在最坏情况下的运行时间是多少? 文心一言,代码正常运行: 在这个问题中,我们 ...

  10. JAVA已过气?中俄大佬对话告诉你俄罗斯最受欢迎的编程语言是什么!

    摘要:中俄大佬对话:俄罗斯最受欢迎的编程语言是什么?Gitee如何抗住数据压力? 众所周知,Java作为一门非常成熟的语言,国内拥趸者众多,但随着后浪们的崛起,如今的Java在国际上是否还占据主流地位 ...