最近,有群里在群里发了这么一个非常有意思的卡片 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. NetSuite 开发日记:创建 Transfer(转账单)

    经测试,截止到 2022.12.26,Transfer 只能使用 Client 脚本创建,使用服务端脚本创建报错:ReferenceError: "document" is not ...

  2. python 获取android 应用使用情况

    python 获取android 应用使用情况 本文主要讲述python 脚本获取android 应用使用情况. 主要思路:使用adb 获取当前activity ,1s 一次输出. 主要涉及知识点: ...

  3. MyBatis中获取参数值的两种方式:${} 和 #{},以及它们之间区别是什么?

    MyBatis中获取参数值的两种方式 ${}:的本质就是字符串拼接 #{}:的本质就是占位符赋值 ① 使用${}占位符,在字符串拼接的方式拼接sql,若为字符串类型或为日期类型的字段进行赋值时,需要手 ...

  4. 由浅入深理解C#中的事件

    目录 本文较长,给大家提供了目录,可以直接看自己感兴趣的部分. 前言 有关事件的概念 示例 ​ 简单示例 ​ 标准 .NET 事件模式 ​ 使用泛型版本的标准 .NET 事件模式 ​ 补充 总结 参考 ...

  5. 原生JavaScript 与 jQuery 执行Ajax请求

    原生JavaScript和jQuery都可以用来执行Ajax请求,以下是它们的基本实现方式的比较: 原生JavaScript实现Ajax请求: var xhr = new XMLHttpRequest ...

  6. 最新消息:OpenAI GPT Store 正式上线,GPTs 应用商店来了!

    OpenAI推出的两款新产品和服务:GPT Store和ChatGPT Team,提供了许多全新的解决方案和功能,旨在帮助用户更轻松地使用和构建GPT工具,同时也增加了公司的收入来源.GPT Stor ...

  7. k8s卷管理-2

    目录 k8s卷管理-2 pv和pvc pv pv的定义 pvc pvc的定义 pv和pvc的绑定关系 手动指定pv与pvc绑定 pod使用pvc pod挂载pvc 访问模式的区别 k8s卷管理-2 之 ...

  8. CSS之动画

    一.动画 动画类型 CSS3 可以创建动画,它可以取代许多网页动画图像.Flash 动画和 JavaScript 实现的效果. transform属性可以定义一些主要的动画属性, translate: ...

  9. maven系列:聚合与继承

    目录 一.聚合 创建Maven模块,设置打包类型为pom 设置当前聚合工程所包含的子模块名称 二. 继承 问题导入 创建Maven模块,设置打包类型为pom 在父工程的pom文件中配置依赖关系(子工程 ...

  10. 一文带你了解华为云GaussDB的五大黑科技

    摘要:在华为开发者大会2021(HDC·Cloud)中,华为云数据库多位技术专家分享了GaussDB系列数据库全新5大黑科技. 4月24-26日,华为开发者大会2021(HDC·Cloud)在深圳隆重 ...