【前端特效系列】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')是通过正弦函数拟火把的自然闪烁效果

🌟 最终效果

相关推荐
To_OC7 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
To_OC7 小时前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
你怎么知道我是队长10 小时前
JavaScript的变量和数据类型介绍
开发语言·javascript·ecmascript
名字还没想好☜11 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司11 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
心中有国也有家11 小时前
AtomGit Flutter 鸿蒙客户端:ModalBottomSheet 实战
android·javascript·学习·flutter·华为·harmonyos
kaiking_g11 小时前
vue项目中,静态导入 vs 动态导入 详解
前端·javascript·vue.js
仙人球部落13 小时前
-python-LangGraph框架(3-31-LangGraph 「合并式状态管理」的原理与实践)
开发语言·javascript·python
sunfdf14 小时前
Next.js 新手从零部署到首跑实战指南
开发语言·javascript·ecmascript
SmartBoyW14 小时前
前端死磕:一文彻底搞懂 JS 事件循环 (Event Loop) 与宏微任务
前端·javascript