Canvas获取文本点位动态拼字特效
一: 应用场景
在一些C端的活动中,我们可能会需要实现一个动态拼字的效果,例如下面这样:
Canvas动态拼字效果视频
3d动态拼字效果视频
二:实现思路:
上面视频中的文本点位信息我们可以通过canvas来获取:
- 将文本绘制在canvas画布上。
- 通过canvas的2d上下文调用getImageData来拿到像素数据。
- 对像素数据进行过滤拿到文本位置的像素信息。
我们在黑色背景的canvas画布上绘制白色的文本,然后循环canvas上的每一个像素,通过像素的颜色值来判断该像素点是属于文本还是背景。
三: 关键代码
1. 简单版:
// 获取canvas的2d上下文
this.canvas = document.getElementById("myCanvas");
this.ctx = this.canvas.getContext("2d");// 存储文本点位数据
this.textPointData = [];
// 调用getImageData方法获取canvas区域的像素数据
let imgData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
let pxl = imgData.data;
// 循环canvas每个像素点,过滤出文字部分的像素信息
for (let i = 0; i < this.canvas.width * this.canvas.height; i++) {let alphaIndex = i * 4 + 3;if (pxl[i * 4 + 3] > 0) {// 根据alpha值获得像素的线性位置。let linearPosition = alphaIndex / 4;// 将线性位置除以宽度以得到Y坐标let y = Math.floor(linearPosition / this.canvas.width);// 将y坐标乘以宽度,并从线性位置减去整个值以得到X坐标。let x = linearPosition - (Math.floor(linearPosition / this.canvas.width) * this.canvas.width);this.textPointData.push({x,y,z: 0,})}}
2. 升级版
增加功能:
- 文本是否填充。
- 相邻点的间距设置。
- 自定义像素过滤条件。
/** 获取文本点位信息 */
export default class TextToPoints {/** 采样的文本 */str: string = "神小夜";/** 是否填充 */fill: boolean = false;/** 相邻点位的间距 */separation: number = 2;/** 像素的过滤函数 */pixelCallback: any;canvas: any;constructor(canvas, str, fill, separation?, pixelCallback?) {this.canvas = canvas;this.str = str;this.fill = fill;this.separation = separation || 2;this.pixelCallback = pixelCallback || this.defaultPixelCallback;}/** 默认pixelCallback:检查是否白色文本在黑色背景 */defaultPixelCallback(color) {return ((color[0] + color[1] + color[2]) / 3 > 50);}/*** 获取每个像素点颜色*/checkPixel(i, imageData) {if (i < 0 || i > imageData.length) return false;const red = imageData[i];const green = imageData[i + 1];const blue = imageData[i + 2];const alpha = imageData[i + 3];const color = [red, green, blue, alpha];return this.pixelCallback(color);};/*** 获取separation间隔的点位数据* @Param separation 相邻点位的间隔距离* @Param width canvas宽* @Param height canvas高* @Param context canvas 2d ctx*/scan(separation, width, height, context) {const imageData = context.getImageData(0, 0, width, height).data;const points = [];const map = {};for (let i = 0; i < height; i++) {for (let j = 0; j < width; j++) {// 拿到每个像素点数据第一位的索引const pos = (i * width + j) * 4;// 过滤获取点位if (this.checkPixel(pos, imageData) && (this.fill ||i == 0 ||i == height - 1 ||j == 0 ||j == width - 1 ||!this.checkPixel(pos - 4, imageData) ||!this.checkPixel(pos + 4, imageData) ||!this.checkPixel(pos - width * 4, imageData) ||!this.checkPixel(pos + width * 4, imageData))) {let alreadyAdded = false;// 循环(i,j)点 的 separation范围内的点for (let inner_i = i - separation; inner_i < i + separation; inner_i++) {for (let inner_j = j - separation; inner_j < j + separation; inner_j++) {if (inner_j == j && inner_i == i) continue;if (map[inner_i + "_" + inner_j]) {alreadyAdded = true;break;}}if (alreadyAdded) break;}// 已经添加的点位,不再push进数组if (!alreadyAdded) {points.push({x: j, y: i});map[i + "_" + j] = true;}}}}return points;};/** 获取点位数据 */getPoints() {const canvas_temp = document.createElement("canvas");const context_temp = canvas_temp.getContext('2d');context_temp.font = "200px Verdana";// 拿到绘制的文本 的width, 并设置heightconst text_measure: any = context_temp.measureText(this.str);text_measure.height = 300;canvas_temp.width = text_measure.width;canvas_temp.height = text_measure.height;context_temp.fillStyle = "black";context_temp.fillRect(0, 0, this.canvas.width, this.canvas.height);context_temp.fillStyle = "white";context_temp.font = "200px Verdana";context_temp.textAlign = "center";context_temp.textBaseline = "middle";context_temp.fillText(this.str, canvas_temp.width / 2, canvas_temp.height / 2);const points = this.scan(this.separation, canvas_temp.width, canvas_temp.height, context_temp);return {points,textW: text_measure.width,textH: text_measure.height,}};}
获取点位数据
const data = new TextToPoints(this.canvas, "神小夜", false).getPoints();
四: 结语
上面的实现方法并不局限于获取文本点位信息,只要是画在canvas上的东西都是同理的。比如logo,头像等等。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
