这个特效主要由 HTMLCSS3JavaScript 协同完成,下面我将为你详细解释其原理,并提供一个可以直接复制使用的完整示例,以及一些更高级的实现思路。

网页特效js css3 鼠标擦除
(图片来源网络,侵删)

核心原理

想象一下你有一张图片(底层),上面覆盖了一层灰色的“保护膜”(上层),你的目标不是点击按钮,而是用鼠标在这层“保护膜”上“刮开”,显示出下面的图片。

这个效果的实现依赖于以下几个关键技术点:

  1. HTML 结构:

    • 一个容器,用于定位和放置内容。
    • 一个底层元素,存放最终要显示的内容(例如图片、文字等)。
    • 一个上层元素,作为可以被“擦除”的遮罩层。
  2. CSS 样式:

    网页特效js css3 鼠标擦除
    (图片来源网络,侵删)
    • 定位: 使用 position: relativeposition: absolute 将底层和上层完美重叠在一起。
    • 遮罩层样式: 上层遮罩层默认覆盖整个底层,并设置一个背景色(如灰色)或背景图片。
    • 关键属性 mix-blend-mode: 这是实现效果的核心,通过设置 mix-blend-mode: destination-out,我们可以让上层元素的绘制区域“挖空”下方的任何内容,当鼠标在这个上层元素上绘制时,绘制的部分就会变成透明,从而显示出底层的图像。
    • 鼠标样式: 将鼠标指针设置为一个自定义的圆形或橡皮擦图标,增强用户体验。
  3. JavaScript 交互:

    • 监听鼠标事件: 监听 mousedown(鼠标按下)、mousemove(鼠标移动)和 mouseup(鼠标抬起)事件。
    • 绘制擦除路径: 当鼠标按下并移动时,在 Canvas 上绘制一个路径,这个路径就是擦除的区域。
    • 使用 Canvas: 我们将上层遮罩层实现为一个 <canvas> 元素,因为 Canvas 提供了强大的 2D 绘图上下文,可以轻松地绘制图形(如圆形),并且其像素级操作与 mix-blend-mode 完美配合。

完整代码示例(可直接复制运行)

这是一个最基础、最经典的实现方式,效果直观,易于理解。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">CSS3 鼠标擦除特效</title>
    <style>
        body {
            margin: 0;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background-color: #f0f0f0;
            font-family: sans-serif;
        }
        .scratch-container {
            position: relative;
            width: 500px;
            height: 300px;
            border-radius: 10px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
            overflow: hidden; /* 确保子元素不会溢出 */
        }
        /* 底层内容 - 最终要显示的内容 */
        .scratch-content {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-image: url('https://images.unsplash.com/photo-1506905925346-21bda4d32df4?q=80&w=1974&auto=format&fit=crop');
            background-size: cover;
            background-position: center;
            display: flex;
            justify-content: center;
            align-items: center;
            color: white;
            font-size: 2.5em;
            font-weight: bold;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
        }
        /* 上层遮罩层 - 使用Canvas实现 */
        #scratchCanvas {
            position: absolute;
            top: 0;
            left: 0;
            cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" fill="none" stroke="black" stroke-width="2"/></svg>') 12 12, auto;
            /* 关键属性:将绘制的区域变为“挖空”模式 */
            mix-blend-mode: destination-out; 
        }
        /* 提示文字 */
        .hint {
            position: absolute;
            bottom: -30px;
            left: 50%;
            transform: translateX(-50%);
            color: #666;
            font-size: 0.9em;
        }
    </style>
</head>
<body>
    <div class="scratch-container">
        <!-- 底层内容 -->
        <div class="scratch-content">
            恭喜你发现了隐藏内容!
        </div>
        <!-- 上层 Canvas 遮罩层 -->
        <canvas id="scratchCanvas"></canvas>
    </div>
    <p class="hint">用鼠标在灰色区域上擦除</p>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const canvas = document.getElementById('scratchCanvas');
            const ctx = canvas.getContext('2d');
            const container = document.querySelector('.scratch-container');
            // 设置 Canvas 的尺寸
            canvas.width = container.offsetWidth;
            canvas.height = container.offsetHeight;
            // 擦除半径
            const radius = 20;
            // 绘制初始遮罩层
            function drawInitialMask() {
                ctx.fillStyle = '#c0c0c0'; // 银灰色遮罩
                ctx.fillRect(0, 0, canvas.width, canvas.height);
            }
            drawInitialMask();
            // 鼠标状态
            let isDrawing = false;
            // 获取鼠标在 Canvas 上的坐标
            function getMousePos(e) {
                const rect = canvas.getBoundingClientRect();
                return {
                    x: e.clientX - rect.left,
                    y: e.clientY - rect.top
                };
            }
            // 开始绘制
            canvas.addEventListener('mousedown', (e) => {
                isDrawing = true;
                const pos = getMousePos(e);
                ctx.beginPath();
                ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
                ctx.fill();
            });
            // 绘制中
            canvas.addEventListener('mousemove', (e) => {
                if (!isDrawing) return;
                const pos = getMousePos(e);
                ctx.beginPath();
                ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
                ctx.fill();
            });
            // 结束绘制
            canvas.addEventListener('mouseup', () => {
                isDrawing = false;
            });
            // 鼠标离开 Canvas 区域时也结束绘制
            canvas.addEventListener('mouseleave', () => {
                isDrawing = false;
            });
        });
    </script>
</body>
</html>

代码解析

  1. HTML:

    • .scratch-container: 一个相对定位的盒子,作为所有内容的舞台。
    • .scratch-content: 绝对定位,覆盖整个容器,这里放了一张背景图片和一行文字,这是被“隐藏”的内容。
    • #scratchCanvas: 绝对定位,也覆盖整个容器,并且位于 .scratch-content 的上方(HTML 顺序决定了层叠顺序,后写的在上层),它就是我们的“灰色保护膜”。
  2. CSS:

    网页特效js css3 鼠标擦除
    (图片来源网络,侵删)
    • position: absolute; top: 0; left: 0;: 确保两个子元素完美重叠。
    • mix-blend-mode: destination-out;: 魔法所在,这个CSS属性定义了元素如何与其背景混合。destination-out 的意思是“显示目标(下方元素)的透明部分”,简单说就是让当前元素绘制的部分变成透明。
    • cursor: ...: 我们用内联的 SVG 创建了一个圆形的橡皮擦光标,让用户体验更佳。
  3. JavaScript:

    • 初始化: 获取 canvas 元素和其 2D 绘图上下文 ctx,设置 canvas 的宽高为容器的宽高(这非常重要,否则会模糊)。
    • drawInitialMask(): 在 Canvas 上填充一个银灰色的矩形,作为初始的遮罩层。
    • 事件监听:
      • mousedown: 当鼠标按下时,设置 isDrawingtrue,并在鼠标位置画一个实心圆,开始“擦除”。
      • mousemove: 当鼠标移动时,isDrawingtrue,就在新位置持续画圆,形成一条擦除的轨迹。
      • mouseupmouseleave: 当鼠标松开或离开 Canvas 区域时,停止绘制。

高级实现思路与变体

掌握了基本原理后,你可以创造出更多有趣的变体:

使用 globalCompositeOperation 替代 mix-blend-mode

mix-blend-mode 是作用于整个元素的,而 Canvas 的 globalCompositeOperation 是作用于绘图操作的,在 mousemove 事件中设置 ctx.globalCompositeOperation = 'destination-out' 也能达到同样的效果,并且更灵活,可以只对绘图操作生效,不影响元素本身。

// 在 mousedown 或 mousemove 事件中
ctx.globalCompositeOperation = 'destination-out'; // 设置合成操作
ctx.beginPath();
ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
ctx.fill();

这种方式在逻辑上更纯粹,因为所有操作都在 Canvas 的绘图上下文中完成。

擦除百分比与自动完成

你可以通过计算 Canvas 上透明像素的数量,来计算出已经擦除了多少百分比,当达到某个阈值(如80%)时,自动清除整个遮罩层。

// 在mouseup事件中调用
function checkCompletion() {
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const pixels = imageData.data;
    let transparentPixels = 0;
    for (let i = 3; i < pixels.length; i += 4) {
        if (pixels[i] === 0) { // alpha 通道为 0 表示透明
            transparentPixels++;
        }
    }
    const percentage = (transparentPixels / (pixels.length / 4)) * 100;
    console.log(`擦除进度: ${percentage.toFixed(2)}%`);
    if (percentage > 80) {
        // 自动清除所有遮罩
        ctx.clearRect(0, 0, canvas.width, canvas.height);
    }
}

改变擦除工具

不一定要用圆形,你可以用方形、星形,甚至是一张图片(比如一个真实的橡皮擦图标)作为擦除工具。

// 使用方形擦除
const brushSize = 40;
ctx.fillRect(pos.x - brushSize/2, pos.y - brushSize/2, brushSize, brushSize);
// 使用图片擦除 (需要先加载图片)
const brushImage = new Image();
brushImage.src = 'brush.png';
// 在 mousedown/mousemove 中
// ctx.drawImage(brushImage, pos.x - brushImage.width/2, pos.y - brushImage.height/2);

擦除粒子效果

当擦除时,不是让像素直接消失,而是让被擦除的像素(比如灰色方块)变成小粒子,然后向上飘散并消失,这需要更复杂的 Canvas 动画,但效果会非常炫酷。

希望这个详细的解释和示例能帮助你理解并创造出属于自己的“鼠标擦除”特效!