这是我参与更文挑战的第9天,活动详情查看: 更文挑战
简介
- 为了更好的使用
canvas
,实现文字粒子效果。 - 主要是通过在缓存中创建一个
canvas
元素节点,把文本绘制在这个canvas
节点上。然后使用getImageData()
函数,获取canvas
上的所有像数对应颜色的值。根据像数颜色值,判断在什么位置生成粒子。
文本绘制
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<style type="text/css"></style>
<body>
<canvas height="400" width="600" id="canvas"></canvas>
</body>
<script>
// 文字粒子
/** @type {HTMLCanvasElement} */
var canvas = document.getElementById('canvas')
var ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, canvas.width, canvas.height)
function loadCanvas(value) {
var fontSize = 100 // 文本大小
// 文本长度
var width = calWordWidth(value, fontSize)
var canvasTxt = document.createElement('canvas')
canvasTxt.width = width
canvasTxt.height = fontSize
var ctxTxt = canvasTxt.getContext('2d')
ctxTxt.font = fontSize + 'px Microsoft YaHei'
ctxTxt.fillStyle = 'orange'
ctxTxt.fillText(value, 0, fontSize - 16) // 调整绘制字符位置
// 放入html中
document.body.appendChild(canvasTxt)
}
// 传入文本
loadCanvas('学习')
/**
* 计算 文本总长度
* */
function calWordWidth(value, fontSize) {
var arr = value.split('')
var reg = /\w/
var width = 0
arr.forEach(function (item, index) {
if (reg.test(item)) {
width += fontSize // 字母宽度
} else {
width += fontSize + 10 // 汉字宽度
}
})
return width
}
</script>
</html>
复制代码
- 这里主要是通过
document.createElement('canvas')
创建了一个节点,然后把文本信息绘制在画布上。
使用getImageData获取像数
getImageData()
方法返回ImageData
对象,该对象拷贝了画布指定矩形的像素数据。- 该对象是个一维数组。因为每个像素的
RGBA 值
是由4个值组成
的。所以一个像数
对应数组的4个值
,并且数组位置是固定的。
// 获取像数
function getImage(canvas, ctx) {
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
console.log('? ~ file: index.html ~ line 39 ~ getImage ~ imageData', imageData)
}
复制代码
添加粒子类
var particles = [] // 所有粒子
/**
* 粒子
* */
function Particle(option) {
this.radius = option.radius || 6
this.color = option.color || '#000'
this.x = option.x || 0
this.y = option.y || 0
this.dynamicRadius = option.radius || 6
}
Particle.prototype.draw = function (ctx) {
var x, y
x = this.x * 3 + 50
y = this.y * 3 + 50
ctx.beginPath()
ctx.arc(x, y, this.dynamicRadius, 0, 2 * Math.PI, false)
ctx.fillStyle = this.color
ctx.fill()
}
Particle.prototype.update = function () {
this.dynamicRadius = 5 + 2 * Math.sin(((new Date() / 1000) % 1000) * this.radius)
}
复制代码
- 创建粒子类,方便后期管理粒子数据。
在画布上绘制粒子
// 生成文本
function loadCanvas(value) {
...
getImage(canvasTxt, ctxTxt)
// 放入html中
// document.body.appendChild(canvasTxt)
}
// 获取像数
function getImage(canvas, ctx) {
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
var diff = 4
var newCanvas = document.getElementById('canvas')
var newCtx = newCanvas.getContext('2d')
// 遍历所有的数组
for (var j = 0; j < canvas.height; j += diff) {
for (var i = 0; i < canvas.width; i += diff) {
// 当有颜色时 默认透明度 是 255
var opacityIndex = (i + j * canvas.width) * 4 + 3
if (imageData.data[opacityIndex] > 0) {
// 放入粒子对象
var par = new Particle({ radius: 0, color: '#000', x: i, y: j })
particles.push(par)
// particles.draw(newCtx)
}
}
}
}
requestAnimationFrame(function loop() {
// requestAnimationFrame(loop)
ctx.clearRect(0, 0, canvas.width, canvas.height)
for (const particle of particles) {
particle.draw(ctx)
}
})
// 传入文本
loadCanvas('学习')
...
复制代码
- 遍历
imageData
对象中的data
,当颜色存在时透明度默认是255,所以这判断的是第4的个值是否不为 0 来判断该像素是否在文本中。 - 得到的粒子对象放入数组中,后期修改粒子就不用重新计算位置。
加入简单动画
requestAnimationFrame(function loop() {
requestAnimationFrame(loop)
ctx.clearRect(0, 0, canvas.width, canvas.height)
for (const particle of particles) {
particle.update()
particle.draw(ctx)
}
})
复制代码
- 因为粒子对象已保存在数组中。我们只需要在
update()
函数中添加动画逻辑,然后重新绘制就能实现动画。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END