Three.js - 利用 JS 進行圖片處理並生成對應粒子圖
平時需要實現幾個的動效來改善無聊的中後臺業務帶來的負面情緒。
概述:利用 JS 以及 Three.js 對下圖進行處理

來生成對應的粒子圖, 例項程式碼 。

主要分為以下幾個步驟
1. 獲取對應影象資訊
首先讀取圖片,可以利用 document.images
獲取頁面中 img
的資訊。
再將 img
繪製到 canvas
畫布,利用 getImageData
獲取影象的畫素資訊,具體如下 getImgData
const getImgData = img => { // 寬、高 const { width, height } = img const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d') // 畫素數 const numPixels = width * height canvas.width = width canvas.height = height ctx.scale(1, -1) ctx.drawImage(img, 0, 0, width, height * -1) // 用來描述canvas區域隱含的畫素資料 const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height) return { width, numPixels, originalColors: Float32Array.from(imgData.data), } } 複製程式碼
imgData.data
包含著圖片的畫素資料的陣列,即 RGBA 值:
- R - 紅色 (0-255);
- G - 綠色 (0-255);
- B - 藍色 (0-255);
- A - alpha 通道 (0-255; 0 是透明的,255 是完全可見的)。
在 imgData.data
排列順序是每個畫素點的 [R, G, B, A, R, G, B, A, ...]
,如圖

2. 影象處理
上一步中獲取到相關影象的畫素資訊,然後處理四個通道的資訊 originalColors
由於影象的背景為黑色,也就是說可以利用 R 通道 的資料來進行閾值分割
設定一個閾值 threshold
,如果滿足 originalColors[i * 4 + 0] > threshold
的條件,則統計該畫素點可見,然後遍歷 originalColors
得到可見畫素點的位置的座標。
const getParticleData = (img, threshold) => { const { width, numPixels, originalColors } = getImgData(img) let numVisible = 0 // 統計大於閾值的畫素點 for (let i = 0; i < numPixels; i++) { if (originalColors[i * 4 + 0] > threshold) numVisible++ } const offsets = new Float32Array(numVisible * 3) // 獲取畫素點的位置 for (let i = 0, j = 0; i < numPixels; i++) { if (originalColors[i * 4 + 0] > threshold) { // 獲取 x 方向的座標 offsets[j * 3 + 0] = i % width // 獲取 y 方向的座標 offsets[j * 3 + 1] = Math.floor(i / width) j++ } } return { offsets } } 複製程式碼
3. 生成粒子圖
這一步比較簡單,就是利用得到的畫素位置來生成對應的粒子圖,首先初始化場景、相機等要素
// init webGL const scene = new THREE.Scene() const group = new THREE.Group() scene.add(group) const camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 10, 10000 ) camera.position.z = 300 const fovHeight = 2 * Math.tan(camera.fov * Math.PI / 180 / 2) * camera.position.z const renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('canvas'), antialias: true, alpha: true, }) renderer.setClearColor(0x000000, 1) 複製程式碼
然後,在 offsets
的位置資料上,生成對應的粒子
設定每個粒子的 material
,然後利用 TweenMax.to
使得粒子過渡至對應位置。
const textureLoader = new THREE.TextureLoader() const map = textureLoader.load('./assets/images/circle.png') const material = new THREE.SpriteMaterial({ map, color: 0xffffff, fog: true }) const positions = offsets for (let index = 0; index < positions.length; index += 2) { const particleMaterial = material const particle = new THREE.Sprite(particleMaterial) // 粒子目標位置 const targetX = positions[index] const targetY = positions[index + 1] const targetZ = positions[index + 2] if (targetX && targetY) { // 粒子的初始位置 particle.position.x = 0 particle.position.y = 0 particle.position.z = 0 // 粒子從初始位置過渡到目標位置 TweenMax.to(particle.position, 1, { x: targetX, y: targetY, z: targetZ, delay: Math.random() * 0.1 }) group.add(particle) } } 複製程式碼
4. 總結
簡單的閾值分割結合粒子特效對影象進行處理,如果需要其他特效,可以處理粒子的 material
,例如 RawShaderMaterial
,可以實現如下效果
