【前端特效系列】css+js实现聚光灯效果

✨ 前言

源码地址:leixq1024/FrontEndSnippetHub: ✨html+css+js的前端特效合集

本次灵感来源:codepen.io/zorgos/pen/...

​ 这个系列主要分享一些用css+html+js制作的前端特效或动画的代码实现思路和解析。如果对你有帮助请给仓库点一个✨

🎬 效果演示

🧰 前期准备

​ 这里我准备了两个图片一个是地图,一个是火把gif,并且创建了index.htmlstyle.cssindex.js三个文件

🗺️ 初始化场景

index.html

html 复制代码
<!DOCTYPE html>
<html lang="zh-cn">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>地图聚光灯</title>
    <!-- <link rel="stylesheet" href="style.css" /> -->
  </head>
  <body>
    <div class="map">
      <!-- 黑色遮罩 -->
      <div class="mask" id="mask"></div>
      <!-- 火把gif,随光圈移动 -->
      <img id="torch" src="./img/torch.gif" alt="火把" />
    </div>
    <script src="index.js"></script>
  </body>
</html>

刚开始没有设置样式效果就如下

🎨 编写样式

先把地图放上去

css 复制代码
html,
body {
  position: relative;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: none;
}
/* 地图 */
.map {
  position: relative;
  width: 100vw;
  height: 100vh;
  background: url('./img/map.png') no-repeat;
  background-size: 100% 100%;
}

效果如下

接下来做一个黑色的背景,并且用mask-image做一个蒙版

css 复制代码
.mask {
  position: absolute;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 1);
  mask-image: radial-gradient(
    circle var(--r, 110px) at var(--x, 50%) var(--y, 50%),
    transparent 0%,
    transparent 50%,
    black 100%
  );
  transition: -webkit-mask-position 0.06s linear;
  transition: mask-position 0.06s linear;
  pointer-events: auto;
}
  • 通过mask-image的径向渐变创建圆形透明区域
  • transparent 0%transparent 50%:中心区域完全透明(显示底层内容)
  • black 100%:边缘黑色区域遮挡内容
  • 最终效果:黑色背景中有一个圆形"窗口"

其中transition: mask-position 0.06s linear;中的mask-position是指遮罩位置,这样遮罩位置变化就会有一个线性的过渡

这里蒙版的一些值用css变量来控制,方便等下用js动态的更新蒙版的位置

效果如下

🔥 火把样式

css 复制代码
/* 火把样式 */
#torch {
  position: absolute;
  width: 100px;
  height: 100px;
  pointer-events: none;
  z-index: 10;
  left: var(--x, 50%);
  top: var(--y, 50%);
  transform: translate(-50%, -50%);
}

效果如下

🖱️ 鼠标和滚轮事件

index.js

javascript 复制代码
let radius = 110 // 光照半径
// 设置css变量
const setStyleVar = (el, key, val) => el && el.style.setProperty(key, val)
// 遮罩元素
const mask = document.getElementById('mask')
// 火把元素
const torch = document.getElementById('torch')
// 修改遮罩层光圈位置
const setPos = (clientX, clientY) => {
  const { left, top } = mask.getBoundingClientRect()
  setStyleVar(mask, '--x', clientX - left + 'px')
  setStyleVar(mask, '--y', clientY - top + 'px')
  // 火把居中显示在光圈圆心
  setStyleVar(torch, '--x', clientX - left + 'px')
  setStyleVar(torch, '--y', clientY - top + 'px')
}
// 鼠标移动时,更新遮罩层光圈位置
mask.addEventListener('mousemove', (e) => {
  setPos(e.clientX, e.clientY)
})
// 滚轮滚动时,更新光照半径
mask.addEventListener('wheel', (e) => {
  radius = Math.max(50, Math.min(200, radius + e.deltaY * 0.1))
  setStyleVar(mask, '--r', radius + 'px')
})
let flickerTime = 0
// 遮罩层呼吸效果
const maskBreathe = () => {
  flickerTime += 0.05
  setStyleVar(mask, '--r', radius + Math.sin(flickerTime * 3) * 3 + 'px')
  requestAnimationFrame(maskBreathe)
}
maskBreathe()

其中 setStyleVar(mask, '--r', radius + Math.sin(flickerTime * 3) * 3 + 'px')是通过正弦函数拟火把的自然闪烁效果

🌟 最终效果

相关推荐
xvmingjiang5 分钟前
Element Plus 中 el-input 限制为数值输入的方法
前端·javascript·vue.js
XboxYan22 分钟前
借助CSS实现自适应屏幕边缘的tooltip
前端·css
狂炫一碗大米饭33 分钟前
事件委托的深层逻辑:当冒泡不够时⁉️
javascript·面试
张柏慈1 小时前
JavaScript性能优化30招
开发语言·javascript·性能优化
pepedd8642 小时前
全面解析this-理解this指向的原理
前端·javascript·trae
渔夫正在掘金2 小时前
神奇魔法类:使用 createMagicClass 增强你的 JavaScript/Typescript 类
前端·javascript
雲墨款哥2 小时前
一个前端开发者的救赎之路-JS基础回顾(三)-Function函数
前端·javascript
艾小码2 小时前
CSS粘性定位失效?深度解析 position: sticky 的陷阱与解决方案
前端·css
汪子熙2 小时前
深入理解 TypeScript 的 /// <reference /> 注释及其用途
前端·javascript
全栈老石2 小时前
设计师到前端不再有墙:Figma + VS Code 自动出码实践
前端·vue.js·html