【前端】【canvas】【pixi.js】水波纹滤镜实现教程 - 支持随机波动与鼠标交互

前言

在Web图形开发中,Canvas技术为我们提供了丰富的视觉表现能力。今天,我将分享如何使用Pixi.js框架及其滤镜系统,实现一个具有随机水波纹和鼠标交互的水波纹特效。本教程基于Pixi.js 7.4.2版本,通过实际代码演示如何创建动态的视觉体验。
实现效果预览:

  • 随机生成的水波纹扩散效果
  • 点击屏幕任意位置触发水波纹
  • 多种滤镜叠加的视觉体验

一、环境搭建与项目创建

shell 复制代码
# 创建Vite项目
npm create vite@latest . -- --template vue

# 安装Pixi.js 7.4.2版本
npm install pixi.js@7.4.2

# 安装Pixi滤镜库
npm install pixi-filters@5.1.0

1.2 版本说明

  • Pixi.js 7.4.2:这是目前稳定的版本,提供了优秀的性能和API支持
  • pixi-filters 5.1.0:官方滤镜库,包含多种特效滤镜

二、完整代码实现

下面是一个完整的Vue单文件组件实现:

注意: 需要删除vite构建项目的基本样式,因为这里将一些全局的样式也写在这里了,所以为了能生效,<style>标签不能使用scope

javascript 复制代码
<template>
  <div class="canvas-container">
    <!-- Pixi.js画布将通过JavaScript动态添加到此处 -->
  </div>
</template>

<script setup>
import { onMounted, onUnmounted } from 'vue';
import * as PIXI from 'pixi.js';
import { ShockwaveFilter } from 'pixi-filters';

// Pixi应用实例
let app = null;

onMounted(() => {
  initPixiApp();
});

onUnmounted(() => {
  // 清理资源
  if (app) {
    app.destroy(true);
    app = null;
  }
});

const initPixiApp = () => {
  // 1. 创建Pixi应用
  app = new PIXI.Application({
    width: window.innerWidth,
    height: window.innerHeight,
    backgroundColor: 0x1099bb, // 背景颜色
    resolution: window.devicePixelRatio || 1, // 适配高清屏幕
    antialias: true, // 开启抗锯齿
    resizeTo: window, // 自适应窗口大小
  });

  // 将Pixi画布添加到DOM
  document.querySelector('.canvas-container').appendChild(app.view);

  // 2. 创建容器
  const container = new PIXI.Container();
  app.stage.addChild(container);

  // 3. 创建背景精灵
  // 注意:你需要准备一张名为tree.jpg的图片放在public/textures目录下
  const texture = PIXI.Texture.from('/textures/tree.jpg');
  const sprite = new PIXI.Sprite(texture);
  
  // 居中并缩放图片
  sprite.anchor.set(0.5);
  sprite.scale.set(0.5);
  sprite.position.set(app.screen.width / 2, app.screen.height / 2 - 300);
  container.addChild(sprite);

  // 4. 创建文字元素
  const text = new PIXI.Text('Hello Tree', {
    fontFamily: "Arial",
    fontSize: 30 + Math.floor(app.screen.width * 0.1),
    align: 'center',
    fill: 0xffffff,
    dropShadow: true,
    dropShadowColor: '#000000',
    dropShadowBlur: 4,
    dropShadowAngle: Math.PI / 2,
    dropShadowDistance: 20
  });
  
  text.anchor.set(0.5);
  text.position.set(app.screen.width / 2, app.screen.height / 2);
  container.addChild(text);

  // 5. 创建置换滤镜(增强波纹效果)
  // 注意:需要一张名为replace.png的置换贴图
  const displaceSprite = PIXI.Sprite.from("/textures/replace.png");
  displaceSprite.scale.set(0.1);
  displaceSprite.texture.baseTexture.wrapMode = PIXI.WRAP_MODES.REPEAT; // 设置为重复模式
  const displaceFilter = new PIXI.DisplacementFilter(displaceSprite);
  container.addChild(displaceSprite);

  // 6. 创建多个水波纹滤镜(实现复杂效果)
  // 第一个水波纹滤镜(主波纹)
  const waveFilter1 = new ShockwaveFilter([
    Math.random() * app.screen.width,
    Math.random() * app.screen.height
  ], {
    radius: 180,      // 波纹半径
    wavelength: 30,   // 波长
    amplitude: 10,    // 振幅
    strength: 100,    // 强度
    speed: 500,       // 扩散速度
    waveLength: 100,
  }, 0);

  // 第二个水波纹滤镜(辅助波纹1)
  const waveFilter2 = new ShockwaveFilter([
    Math.random() * app.screen.width,
    Math.random() * app.screen.height
  ], {
    radius: 40,
    wavelength: 30,
    amplitude: 10,
    strength: 100,
    speed: 200,
    waveLength: 100,
  }, 0);

  // 第三个水波纹滤镜(辅助波纹2)
  const waveFilter3 = new ShockwaveFilter([
    Math.random() * app.screen.width,
    Math.random() * app.screen.height
  ], {
    radius: 40,
    wavelength: 30,
    amplitude: 10,
    strength: 100,
    speed: 200,
    waveLength: 100,
  }, 0);

  // 7. 应用所有滤镜
  container.filters = [displaceFilter, waveFilter1, waveFilter2, waveFilter3];

  // 8. 动画循环
  app.ticker.add((delta) => {
    // 更新置换贴图位置(创建流动效果)
    displaceSprite.position.x += 1;
    displaceSprite.position.y += 1;
    
    // 更新各个水波纹滤镜
    updateWaveFilter(waveFilter1, 1);
    updateWaveFilter(waveFilter2, 1.5);
    updateWaveFilter(waveFilter3, 2);
  });

  // 9. 鼠标点击交互
  app.view.addEventListener('click', (e) => {
    // 点击时重置第一个水波纹的位置和时间
    waveFilter1.center = [e.clientX, e.clientY];
    waveFilter1.time = 0;
  });

  // 10. 窗口大小调整处理
  window.addEventListener('resize', () => {
    app.renderer.resize(window.innerWidth, window.innerHeight);
    sprite.position.set(app.screen.width / 2, app.screen.height / 2 - 300);
    text.position.set(app.screen.width / 2, app.screen.height / 2);
  });
};

// 更新水波纹滤镜的辅助函数
function updateWaveFilter(filter, resetTime) {
  // 更新时间,控制波纹扩散
  filter.time += 0.01;
  
  // 重置时间并随机位置
  if (filter.time > resetTime) {
    filter.time = 0;
    filter.center = [
      Math.random() * app.screen.width,
      Math.random() * app.screen.height
    ];
  }
}
</script>

<style>
.canvas-container {
  width: 100vw;
  height: 100vh;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

/* 重置全局样式 */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  margin: 0;
  overflow: hidden;
}

/* 确保Canvas占满屏幕 */
canvas {
  display: block;
  width: 100vw;
  height: 100vh;
}
</style>

三、核心代码解析

3.1 ShockwaveFilter参数详解

javascript 复制代码
const waveFilter = new ShockwaveFilter([
  centerX, centerY  // 波纹中心位置
], {
  radius: 180,       // 波纹最大半径
  wavelength: 30,    // 波长,控制波纹间距
  amplitude: 10,     // 振幅,控制波纹高度
  strength: 100,     // 强度,影响扭曲程度
  speed: 500,        // 波纹扩散速度
  waveLength: 100,   // 初始波长
}, 0); // 初始时间

3.2 随机水波纹实现原理

javascript 复制代码
// 通过定时重置滤波器中心位置实现随机波纹
function updateWaveFilter(filter, resetTime) {
  filter.time += 0.01;
  if(filter.time > resetTime) {
    filter.time = 0;
    // 随机生成新的波纹中心
    filter.center = [
      Math.random() * app.screen.width,
      Math.random() * app.screen.height
    ];
  }
}

3.3 鼠标交互实现

javascript 复制代码
app.view.addEventListener('click', (e) => {
  // 将鼠标点击坐标设置为波纹中心
  waveFilter1.center = [e.clientX, e.clientY];
  waveFilter1.time = 0; // 重置时间开始新波纹
});

总结

通过本教程,我们学习了:

  1. Pixi.js基础应用创建:初始化、精灵创建、容器管理
  2. 滤镜系统使用:ShockwaveFilter水波纹滤镜和DisplacementFilter置换滤镜
  3. 动画循环实现:使用app.ticker创建动态效果
  4. 交互功能添加:响应鼠标点击事件

附件

上面使用到了DisplacementFilter置换滤镜,这是需要一张置换滤镜的图片的,在这里附带上了,主要是通过黑白色的图片进行置换.

相关推荐
han_5 小时前
手把手教你写一个VSCode插件,从开发到发布全流程
前端·javascript·visual studio code
爱吃大芒果5 小时前
Flutter 状态管理全家桶:Provider、Bloc、GetX 实战对比
开发语言·前端·javascript·flutter·华为·ecmascript
未知原色5 小时前
react实现虚拟键盘支持Ant design Input和普通input Dom元素-升级篇
前端·javascript·react.js·node.js·计算机外设
qq_381454995 小时前
设计模式详解:代码架构的艺术
开发语言·javascript·ecmascript
半桶水专家5 小时前
vue3中v-model 用法详解
前端·javascript·vue.js
行走的陀螺仪5 小时前
Vue3 项目单元测试全指南:价值、Vitest 落地与提效方案
开发语言·前端·单元测试·html5·vitest
AI浩13 小时前
【Labelme数据操作】LabelMe标注批量复制工具 - 完整教程
运维·服务器·前端
涔溪14 小时前
CSS 网格布局(Grid Layout)核心概念、基础语法、常用属性、实战示例和进阶技巧全面讲解
前端·css
2401_8784545314 小时前
浏览器工作原理
前端·javascript