之前我写了一篇 《明日方舟》前端界面复刻,其中有一个每日签到页面有一个聚光的效果,这次我们来分析一下具体是如何实现的。
效果演示
实际效果如图所示,移动到网格其中某个地方时,周围会出现聚光效果:
而在游戏里面,似乎采用了静态贴图,这可能是由于手游没有这样的交互动作,所以显得有些差强人意。
在同样的 win10 里面,也有类似的效果:
这一次,我们试着在 web 里面实现类似的效果。
web 仿签到日历 copepen: READ MORE+
《明日方舟》前端界面复刻文章地址: READ MORE+
arknights-react 演示地址: READ MORE+
arknights-react 每日签到源码: READ MORE+
实现思路
步骤分为 4 步。
1. 分层
分开两层图层,数字层与网格层。
让数字层放在网格层之上,大概就是这种感觉。
实际 dom 的排列顺序保持一致即可,两个层的内容重叠,大小也一致。
2. 绘制
在数字层绘制初内容,在网格层绘制出网格。
// react hook
function Grid () {
// 生成 60 个网格,其实多少无所谓,随便来。
const [list, setList] = React.useState(
Array.from({ length: 60 }, (_, i) => i + 1)
)
return (
<div className="grid">
<!-- 第一层:网格 -->
<ul className="grid-list grid-border">
{ list.map(item => <li className="grid-item" key={item}></li>) }
</ul>
<!-- 第二层:数字 -->
<ul className="grid-list grid-num">
{ list.map(item => <li className="grid-item" tabindex="0" key={item}>{item}</li>) }
</ul>
</div>
)
}
ReactDOM.render(
<Grid></Grid>,
document.getElementById('root')
)
效果如下
蓝色边框表示我们的网格,而数字这是单独一层展示。
copepen 演示: READ MORE+
3. 显示遮罩
添加遮罩(mask)显示。
原理是用 mask 的其中 4 个属性来控制遮罩。
另外加上点细节丰富一下,让默认的文字是暗色,悬浮的是白色。
.grid-border {
// mask 大小
-webkit-mask-size: 240px 240px;
// mask 不重复
-webkit-mask-repeat: no-repeat;
// mask 圆半径
-webkit-mask-image: radial-gradient(circle, #fff, transparent 120px);
// mask 位置
-webkit-position: 0 0;
}
效果如下:
copepen 演示: READ MORE+。
4. 控制遮罩(mask)移动。
控制遮罩(mask)移动。
主要是控制 mask 的 position,因此我们需要 (x, y) 两个坐标来决定 mask 的位置。
同时鼠标在网格上移动,以及离开的时候,来设置 (x, y) 的位置。
// react hook 实现
function Grid() {
const [list, setList] = React.useState(
Array.from({ length: 60 }, (_, i) => i + 1)
)
const $border = React.useRef(null)
const [x, setX] = React.useState(-500)
const [y, setY] = React.useState(-500)
// 将遮罩移动到鼠标位置
const handleMouseMove = (e) => {
e.stopPropagation()
const rect = $border.current
? $border.current.getBoundingClientRect()
: null
setX(e.pageX - (rect ? rect.x : -500) - 150)
setY(e.pageY - (rect ? rect.y : -500) - 150)
}
//设置遮罩隐藏
const handleMouseLeave = () => {
setX(-500)
setY(-500)
}
return (
<div
className='grid'
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
>
<ul
ref={$border}
className='grid-list grid-border'
style={{
WebkitMaskPosition: `${x}px ${y}px`, // 此处设置 mask 样式
maskPosition: `${x}px ${y}px`,
}}
>
{list.map((item) => (
<li className='grid-item' key={item}></li>
))}
</ul>
<ul className='grid-list grid-num'>
{list.map((item) => (
<li className='grid-item' tabindex='0' key={item}>
{item}
</li>
))}
</ul>
</div>
)
}
// ...
效果如下:
copepen 演示: READ MORE+
以上就是完整的效果啦!
总结
完成日历效果总共需要 4 个步骤:
- 分开两层图层,数字层与网格层。
- 在数字层绘制初内容,在网格层绘制出网格。
- 添加遮罩(mask)显示。
- 控制遮罩(mask)移动。
至于其它的页面布局细节,这里就不多做介绍了,如果有想了解的,可以看源码,也可以在下面评论。
源码地址
web 仿签到日历 copepen: READ MORE+
《明日方舟》前端界面复刻文章地址: READ MORE+
arknights-react 演示地址: READ MORE+
arknights-react 每日签到源码: READ MORE+
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。