Canvas 封装
Canvas 封装
记录一个Canvas封装,这个是给小程序用的,html可以直接用html2canvas
,且不说小程序有没有对应的东西,小程序这原生的东西对于第三方框架都不一定有用,毕竟小程序就是一坨,用不了带document的东西,啥新特性都没有,还一堆bug不修
这里使用的是Taro vue3 typescript
,用别的框架把Taro相关的东西改掉就行
直接搞一个工具类出来
export class Canvas2DHelper
{
}
创建离屏Canvas
离屏Canvas在小程序里面跟正常的Canvas的api基本一致,但是在html里面区别蛮大的,我这里是给小程序用的,自己注意
/**
* 创建离屏canvas
* @param width 宽
* @param height 高
* @returns
*/
public static CreateOffscreenCanvas(width: number = 0, height: number = 0): Taro.OffscreenCanvas
{
let canvas: Taro.OffscreenCanvas = Taro.createOffscreenCanvas({
type: "2d",
});
return canvas;
}
保存和读取
基本操作,首先要保存初始化之后的状态,之后每次操作结束后先读取再保存
/**
* 保存canvas
* @param canvas
*/
public static SaveCanvas(canvas: Canvas): void
{
let canvasContext = canvas.getContext("2d");
//保存状态
canvasContext.save();
}
/**
* 恢复canvas
* @param canvas
*/
public static ReloadCanvas(canvas: Canvas): void
{
let canvasContext = canvas.getContext("2d");
//恢复状态
canvasContext.restore();
}
设置像素比
我觉得这个算是比较没用的,似乎并不能让像素对上,更糊了,而且之后的操作都要除以2,怪麻烦的,不如嗯写就完事了
/**
* 设置canvas像素
* @param canvas
* @param devicePixelRatio 设备像素比
* @param deviceWidth 设备宽
* @param deviceHeight 设备高
*/
public static SetPixelRatio(canvas: Canvas, devicePixelRatio: number, deviceWidth: number, deviceHeight: number): void
{
let canvasContext = canvas.getContext("2d");
canvas.height = deviceHeight * devicePixelRatio;
canvas.width = deviceWidth * devicePixelRatio;
canvasContext.scale(devicePixelRatio, devicePixelRatio);
}
裁剪
/**
* 裁剪canvas
* @param canvas
*/
public static ClipCanvas(canvas: Canvas): void
{
let canvasContext = canvas.getContext("2d");
//裁剪canvas
canvasContext.clip();
}
绘制矩形
绘制矩形还是很简单的,不过为了规范和方便,先整一个Option类出来
我这里单纯绘制图形是没有颜色的,只是闭合路径,方便后续操作
export class CreateRectangleOptionModel
{
private _canvas: Canvas = null;
/**
* 画布
*/
public get Canvas(): Canvas
{
return this._canvas;
}
public set Canvas(v: Canvas)
{
this._canvas = v;
}
private _width: number = 0;
/**
* 宽度
*/
public get Width(): number
{
return this._width;
}
public set Width(v: number)
{
this._width = v;
}
private _height: number = 0;
/**
* 高度
*/
public get Height(): number
{
return this._height;
}
public set Height(v: number)
{
this._height = v;
}
private _x: number = 0;
/**
* 起始坐标x
*/
public get X(): number
{
return this._x;
}
public set X(v: number)
{
this._x = v;
}
private _y: number = 0;
/**
* 起始坐标Y
*/
public get Y(): number
{
return this._y;
}
public set Y(v: number)
{
this._y = v;
}
constructor()
{
this.Canvas = null;
this.Width = 0;
this.Height = 0;
this.X = 0;
this.Y = 0;
}
}
再把这个类作为参数传进来
/**
* 绘制矩形
* @param option
*/
public static CreateRectangle(option: CreateRectangleOptionModel): void
{
let canvas = option.Canvas;
let width = option.Width;
let height = option.Height;
let x = option.X;
let y = option.Y;
let canvasContext = canvas.getContext("2d");
//选择背景色
canvasContext.fillStyle = backgroundColor;
//绘制矩形
canvasContext.fillRect(x, y, width, height);
}
绘制矩形并描边
这里再来一个配置类,其实大部分属性跟矩形的配置是一样的,可以用继承
export class CreateRectangleStrokeColorOptionModel extends CreateRectangleOptionModel
{
private _strokeColor: string = "#000000";
/**
* 描边颜色
*/
public get StrokeColor(): string
{
return this._strokeColor;
}
public set StrokeColor(v: string)
{
this._strokeColor = v;
}
private _lineWidth: number = 1;
/**
* 描边宽度
*/
public get LineWidth(): number
{
return this._lineWidth;
}
public set LineWidth(v: number)
{
this._lineWidth = v;
}
constructor()
{
super();
this.StrokeColor = "#000000";
this.LineWidth = 1;
}
}
对应的函数
/**
* 绘制矩形,描边
* @param option
*/
public static CreateRectangleStrokeColor(option: CreateRectangleStrokeColorOptionModel): void
{
let canvas = option.Canvas;
let width = option.Width;
let height = option.Height;
let x = option.X;
let y = option.Y;
let color = option.StrokeColor;
let lineWidth = option.LineWidth;
let createRectangleOptionModel = new CreateRectangleOptionModel();
createRectangleOptionModel.Canvas = canvas;
createRectangleOptionModel.Width = width;
createRectangleOptionModel.Height = height;
createRectangleOptionModel.X = x;
createRectangleOptionModel.Y = y;
//绘制矩形
Canvas2DHelper.CreateRectangle(createRectangleOptionModel);
let canvasContext = canvas.getContext("2d");
//描边宽度
canvasContext.lineWidth = lineWidth;
//选择描边颜色
canvasContext.strokeStyle = color;
//描边
canvasContext.stroke();
}
绘制矩形并填充颜色
export class CreateRectangleFillBackgroundColorOptionModel extends CreateCircleOptionModel
{
private _backgroundColor: string;
/**
* 背景颜色
*/
public get BackgroundColor(): string
{
return this._backgroundColor;
}
public set BackgroundColor(v: string)
{
this._backgroundColor = v;
}
constructor()
{
super();
this.BackgroundColor = "#000000";
}
}
/**
* 绘制矩形填充背景色
* @param option
*/
public static CreateRectangleBackgroundColor(option: CreateRectangleFillBackgroundColorOptionModel): void
{
let canvas = option.Canvas;
let width = option.Width;
let height = option.Height;
let x = option.X;
let y = option.Y;
let backgroundColor = option.BackgroundColor;
let canvasContext = canvas.getContext("2d");
//选择背景色
canvasContext.fillStyle = backgroundColor;
//绘制矩形
canvasContext.fillRect(x, y, width, height);
//填充背景色
canvasContext.fill();
}
绘制圆形
export class CreateCircleOptionModel
{
private _canvas: Canvas = null;
/**
* 画布
*/
public get Canvas(): Canvas
{
return this._canvas;
}
public set Canvas(v: Canvas)
{
this._canvas = v;
}
private _x: number = 0;
/**
* 圆心坐标x
*/
public get X(): number
{
return this._x;
}
public set X(v: number)
{
this._x = v;
}
private _y: number = 0;
/**
* 圆心坐标Y
*/
public get Y(): number
{
return this._y;
}
public set Y(v: number)
{
this._y = v;
}
private _radius: number = 0;
/**
* 半径
*/
public get Radius(): number
{
return this._radius;
}
public set Radius(v: number)
{
this._radius = v;
}
constructor()
{
this.Canvas = null;
this.X = 0;
this.Y = 0;
this.Radius = 0;
}
}
/**
* 绘制圆形
* @param option
*/
public static CreateCircle(option: CreateCircleOptionModel): void
{
let canvas = option.Canvas;
let x = option.X;
let y = option.Y;
let radius = option.Radius;
let canvasContext = canvas.getContext("2d");
//开始路径
canvasContext.beginPath();
//绘制圆形
canvasContext.arc(x, y, radius, 0, 2 * Math.PI);
//关闭路径
canvasContext.closePath();
}
绘制圆形并描边
export class CreateCircleStrokeColorOptionModel extends CreateCircleOptionModel
{
private _strokeColor: string = "#000000";
/**
* 描边颜色
*/
public get StrokeColor(): string
{
return this._strokeColor;
}
public set StrokeColor(v: string)
{
this._strokeColor = v;
}
private _lineWidth: number = 1;
/**
* 描边宽度
*/
public get LineWidth(): number
{
return this._lineWidth;
}
public set LineWidth(v: number)
{
this._lineWidth = v;
}
constructor()
{
super();
this.StrokeColor = "#000000";
this.LineWidth = 1;
}
}
/**
* 绘制圆形,描边
* @param option
*/
public static CreateCircleStrokeColor(option: CreateCircleStrokeColorOptionModel): void
{
let canvas = option.Canvas;
let x = option.X;
let y = option.Y;
let radius = option.Radius;
let color = option.StrokeColor;
let lineWidth = option.LineWidth;
let createCircleOptionModel = new CreateCircleOptionModel();
createCircleOptionModel.Canvas = canvas;
createCircleOptionModel.X = x;
createCircleOptionModel.Y = y;
createCircleOptionModel.Radius = radius;
//绘制圆形
Canvas2DHelper.CreateCircle(createCircleOptionModel);
let canvasContext = canvas.getContext("2d");
//描边宽度
canvasContext.lineWidth = lineWidth;
//选择描边颜色
canvasContext.strokeStyle = color;
//描边
canvasContext.stroke();
}
绘制圆形并填充颜色
export class CreateCircleFillBackgroundColorOptionModel extends CreateCircleOptionModel
{
private _backgroundColor: string = "#000000";
/**
* 描边颜色
*/
public get BackgroundColor(): string
{
return this._backgroundColor;
}
public set BackgroundColor(v: string)
{
this._backgroundColor = v;
}
constructor()
{
super();
this.BackgroundColor = "#000000";
}
}
/**
* 绘制圆形,填充颜色
* @param option
*/
public static CreateCircleFillBackgroundColor(option: CreateCircleFillBackgroundColorOptionModel)
{
let canvas = option.Canvas;
let x = option.X;
let y = option.Y;
let radius = option.Radius;
let backgroundColor = option.BackgroundColor;
let createCircleOptionModel = new CreateCircleOptionModel();
createCircleOptionModel.Canvas = canvas;
createCircleOptionModel.X = x;
createCircleOptionModel.Y = y;
createCircleOptionModel.Radius = radius;
//绘制圆形
Canvas2DHelper.CreateCircle(createCircleOptionModel);
let canvasContext = canvas.getContext("2d");
//选择背景色
canvasContext.fillStyle = backgroundColor;
//填充背景色
canvasContext.fill();
}
绘制圆角矩形
export class CreateRoundRectangleOptionModel
{
private _canvas: Canvas = null;
/**
* 画布
*/
public get Canvas(): Canvas
{
return this._canvas;
}
public set Canvas(v: Canvas)
{
this._canvas = v;
}
private _width: number = 0;
/**
* 宽度
*/
public get Width(): number
{
return this._width;
}
public set Width(v: number)
{
this._width = v;
}
private _height: number = 0;
/**
* 高度
*/
public get Height(): number
{
return this._height;
}
public set Height(v: number)
{
this._height = v;
}
private _x: number = 0;
/**
* 起始坐标x
*/
public get X(): number
{
return this._x;
}
public set X(v: number)
{
this._x = v;
}
private _y: number = 0;
/**
* 起始坐标Y
*/
public get Y(): number
{
return this._y;
}
public set Y(v: number)
{
this._y = v;
}
private _radius: number = 0;
/**
* 圆角半径
*/
public get Radius(): number
{
return this._radius;
}
public set Radius(v: number)
{
this._radius = v;
}
constructor()
{
this.Canvas = null;
this.Width = 0;
this.Height = 0;
this.X = 0;
this.Y = 0;
this.Radius = 0;
}
}
/**
* 绘制圆角矩形
* @param option
*/
public static CreateRoundRectangle(option: CreateRoundRectangleOptionModel): void
{
let canvas = option.Canvas;
let width = option.Width;
let height = option.Height;
let x = option.X;
let y = option.Y;
let radius = option.Radius;
let canvasContext = canvas.getContext("2d");
//开始路径
canvasContext.beginPath();
//左上角的右上点
canvasContext.moveTo(x + radius, y);
//右上角圆角
canvasContext.arcTo(x + width, y, x + width, y + height, radius);
//右下角圆角
canvasContext.arcTo(x + width, y + height, x, y + height, radius);
//左下角圆角
canvasContext.arcTo(x, y + height, x, y, radius);
//左上角圆角
canvasContext.arcTo(x, y, x + width, y, radius);
//合并路径
canvasContext.closePath();
}
绘制圆角矩形并描边
export class CreateRoundRectangleStrokeColorOptionModel extends CreateRoundRectangleOptionModel
{
private _strokeColor: string = "#000000";
/**
* 描边颜色
*/
public get StrokeColor(): string
{
return this._strokeColor;
}
public set StrokeColor(v: string)
{
this._strokeColor = v;
}
private _lineWidth: number = 1;
/**
* 描边宽度
*/
public get LineWidth(): number
{
return this._lineWidth;
}
public set LineWidth(v: number)
{
this._lineWidth = v;
}
constructor()
{
super();
this.StrokeColor = "#000000";
this.LineWidth = 1;
}
}
/**
* 绘制圆角矩形,描边
* @param option
*/
public static CreateRoundRectangleStrokeColor(option: CreateRoundRectangleStrokeColorOptionModel): void
{
let canvas = option.Canvas;
let width = option.Width;
let height = option.Height;
let x = option.X;
let y = option.Y;
let radius = option.Radius;
let color = option.StrokeColor;
let lineWidth = option.LineWidth;
let createRoundRectangleOptionModel = new CreateRoundRectangleOptionModel();
createRoundRectangleOptionModel.Canvas = canvas;
createRoundRectangleOptionModel.Width = width;
createRoundRectangleOptionModel.Height = height;
createRoundRectangleOptionModel.X = x;
createRoundRectangleOptionModel.Y = y;
createRoundRectangleOptionModel.Radius = radius;
//绘制矩形
Canvas2DHelper.CreateRoundRectangle(createRoundRectangleOptionModel);
let canvasContext = canvas.getContext("2d");
//描边宽度
canvasContext.lineWidth = lineWidth;
//选择描边颜色
canvasContext.strokeStyle = color;
//描边
canvasContext.stroke();
}
绘制圆角矩形并填充颜色
export class CreateRoundRectangleFillBackgroundColorOptionModel extends CreateRoundRectangleOptionModel
{
private _backgroundColor: string;
/**
* 背景颜色
*/
public get BackgroundColor(): string
{
return this._backgroundColor;
}
public set BackgroundColor(v: string)
{
this._backgroundColor = v;
}
constructor()
{
super();
this.BackgroundColor = "#000000";
}
}
/**
* 绘制圆角矩形,填充颜色
* @param option
*/
public static CreateRoundRectangleFillBackgroundColor(option: CreateRoundRectangleFillBackgroundColorOptionModel): void
{
let canvas = option.Canvas;
let width = option.Width;
let height = option.Height;
let x = option.X;
let y = option.Y;
let radius = option.Radius;
let backgroundColor = option.BackgroundColor;
let createRoundRectangleOptionModel = new CreateRoundRectangleOptionModel();
createRoundRectangleOptionModel.Canvas = canvas;
createRoundRectangleOptionModel.Width = width;
createRoundRectangleOptionModel.Height = height;
createRoundRectangleOptionModel.X = x;
createRoundRectangleOptionModel.Y = y;
createRoundRectangleOptionModel.Radius = radius;
//绘制矩形
Canvas2DHelper.CreateRoundRectangle(createRoundRectangleOptionModel);
let canvasContext = canvas.getContext("2d");
//选择背景色
canvasContext.fillStyle = backgroundColor;
//填充背景色
canvasContext.fill();
}
绘制文字
绘制文字是比较麻烦的,因为有换行和省略号的问题
我这里是没有配置字体的,要配置字体就自己稍微改改
export class CreateTextOptionModel
{
private _canvas: Canvas = null;
/**
* 画布
*/
public get Canvas(): Canvas
{
return this._canvas;
}
public set Canvas(v: Canvas)
{
this._canvas = v;
}
private _text: string = "";
/**
* 文本
*/
public get Text(): string
{
return this._text;
}
public set Text(v: string)
{
this._text = v;
}
private _fontSize: number = 14;
/**
* 字体大小
*/
public get FontSize(): number
{
return this._fontSize;
}
public set FontSize(v: number)
{
this._fontSize = v;
}
private _fontWeight: number = 500;
/**
* 字体粗细
*/
public get FontWeight(): number
{
return this._fontWeight;
}
public set FontWeight(v: number)
{
this._fontWeight = v;
}
private _fontColor: string = "#000000";
/**
* 字体颜色
*/
public get FontColor(): string
{
return this._fontColor;
}
public set FontColor(v: string)
{
this._fontColor = v;
}
private _isLineBreak: boolean = false;
/**
* 是否换行
*/
public get IsLineBreak(): boolean
{
return this._isLineBreak;
}
public set IsLineBreak(v: boolean)
{
this._isLineBreak = v;
}
private _isOVerflowEllipsis: boolean = false;
/**
* 超出是否用省略号
*/
public get IsOverflowEllipsis(): boolean
{
return this._isOVerflowEllipsis;
}
public set IsOverflowEllipsis(v: boolean)
{
this._isOVerflowEllipsis = v;
}
private _lineCount: number = 1;
/**
* 行数
*/
public get LineCount(): number
{
return this._lineCount;
}
public set LineCount(v: number)
{
this._lineCount = v;
}
private _lineWidth: number = 100;
/**
* 行宽
*/
public get LineWidth(): number
{
return this._lineWidth;
}
public set LineWidth(v: number)
{
this._lineWidth = v;
}
private _lineHeight: number = 21;
/**
* 行高
*/
public get LineHeight(): number
{
return this._lineHeight;
}
public set LineHeight(v: number)
{
this._lineHeight = v;
}
private _x: number = 0;
/**
* 基线坐标x
*/
public get X(): number
{
return this._x;
}
public set X(v: number)
{
this._x = v;
}
private _y: number = 0;
/**
* 基线坐标Y
*/
public get Y(): number
{
return this._y;
}
public set Y(v: number)
{
this._y = v;
}
constructor()
{
this.Canvas = null;
this.Text = "";
this.FontSize = 14;
this.FontWeight = 500;
this.FontColor = "#000000";
this.IsLineBreak = false;
this.IsOverflowEllipsis = false;
this.LineCount = 1;
this.LineWidth = 100;
this.LineHeight = 21;
this.X = 0;
this.Y = 0;
}
}
/**
* 绘制文字
* @param option
* @returns 返回最大行宽
*/
public static CreateText(option: CreateTextOptionModel): number
{
let maxLineWidth = 0;
let lineWidthList: Array<number> = [];
let textList: Array<string> = [];
let canvas = option.Canvas;
let text = option.Text;
let fontSize = option.FontSize;
let fontWeight = option.FontWeight;
let fontColor = option.FontColor;
let isLineBreak = option.IsLineBreak;
let isOverflowEllipsis = option.IsOverflowEllipsis;
let lineCount = option.LineCount;
let lineWidth = option.LineWidth;
let lineHeight = option.LineHeight;
let x = option.X;
let y = option.Y;
let canvasContext = canvas.getContext("2d");
//选择字体颜色
canvasContext.fillStyle = fontColor;
//字体大小
canvasContext.font = `${fontWeight} ${fontSize}px sans-serif`;
if (true == isLineBreak)
{
//换行
if (true == isOverflowEllipsis)
{
//有省略号
textList = Canvas2DHelper.GetLineBreakWithOverflowEllipsisTextList(canvas, text, fontSize, fontWeight, lineWidth, lineCount);
}
else
{
//无省略号
textList = Canvas2DHelper.GetLineBreakTextList(canvas, text, fontSize, fontWeight, lineWidth);
}
}
else
{
//不换行
if (true == isOverflowEllipsis)
{
//有省略号
textList = Canvas2DHelper.GetLineBreakWithOverflowEllipsisTextList(canvas, text, fontSize, fontWeight, lineWidth, 1);
}
else
{
//无省略号
textList = [text];
}
}
//循环绘制文本
for (let i = 0; i < textList.length; i++)
{
const rowText = textList[i];
let rowTextCanvas = canvasContext.measureText(rowText);
let rowTextWidth = rowTextCanvas.width;
lineWidthList.push(rowTextWidth);
let rowY = y + (i * lineHeight);
//绘制文本
canvasContext.fillText(rowText, x, rowY);
}
//最大行宽
maxLineWidth = Math.max(lineWidthList);
return maxLineWidth;
}
/**
* 获取换行字符串
* @param canvas
* @param text 文本
* @param fontSize 字体大小
* @param fontWeight 字体粗细
* @param lineWidth 行宽
* @returns
*/
private static GetLineBreakTextList(canvas: Canvas, text: string, fontSize: number, fontWeight: number, lineWidth: number): Array<string>
{
let textList: Array<string> = [];
let canvasContext = canvas.getContext("2d");
//字体大小
canvasContext.font = `${fontWeight} ${fontSize}px sans-serif`;
let textLength = text.length;
let entireText = text;
let currentRowText = "";
for (let i = 0; i < text.length; i++)
{
let textItem = text[i];
currentRowText += textItem;
let currentRowTextCanvas = canvasContext.measureText(currentRowText);
let currentRowTextWidth = currentRowTextCanvas.width;
if (currentRowTextWidth > lineWidth)
{
currentRowText = currentRowText.substring(0, currentRowText.length - 1);
//换行
textList.push(currentRowText);
//初始化行数据
currentRowText = "";
i--;
entireText = text.substring(i, textLength);
}
}
//最后一行
textList.push(currentRowText);
return textList;
}
/**
* 获取换行省略号字符串
* @param canvas
* @param text 文本
* @param fontSize 字体大小
* @param fontWeight 字体粗细
* @param lineWidth 行宽
* @param lineCount 最大行数
* @returns
*/
private static GetLineBreakWithOverflowEllipsisTextList(canvas: Canvas, text: string, fontSize: number = 14, fontWeight: number, lineWidth: number, lineCount: number = 1): Array<string>
{
let textList: Array<string> = [];
let canvasContext = canvas.getContext("2d");
//字体大小
canvasContext.font = `${fontWeight} ${fontSize}px sans-serif`;
let isAddLast = true;
let textLength = text.length;
let entireText = text;
let currentRowText = "";
for (let i = 0; i < text.length; i++)
{
let textItem = text[i];
currentRowText += textItem;
let currentRowTextCanvas = canvasContext.measureText(currentRowText);
let currentRowTextWidth = currentRowTextCanvas.width;
if (currentRowTextWidth > lineWidth)
{
currentRowText = currentRowText.substring(0, currentRowText.length - 1);
let currentRowCount = textList.length + 1;
if (currentRowCount < lineCount)
{
//换行
textList.push(currentRowText);
//初始化行数据
currentRowText = "";
i--;
entireText = text.substring(i, textLength);
}
else
{
let lastRowText = Canvas2DHelper.GetLineBreakWithOverflowEllipsisText(canvas, currentRowText, fontSize, fontWeight, lineWidth);
textList.push(lastRowText);
isAddLast = false;
break;
}
}
}
if (true == isAddLast)
{
textList.push(currentRowText);
}
return textList;
}
/**
* 获取增加省略号之后的字符串
* @param canvas
* @param text 文本
* @param fontSize 字体大小
* @param fontWeight 字体粗细
* @param lineWidth 行宽
* @returns
*/
private static GetLineBreakWithOverflowEllipsisText(canvas: Canvas, text: string, fontSize: number = 14, fontWeight: number, lineWidth: number): string
{
let ellipsisText = "";
let canvasContext = canvas.getContext("2d");
//字体大小
canvasContext.font = `${fontWeight} ${fontSize}px sans-serif`;
let ellipsis = "...";
let ellipsisLength = ellipsis.length;
let currentRowText = text;
for (let i = text.length - 1; i >= 0; i--)
{
const textItem = text[i];
currentRowText += ellipsis;
let currentRowTextCanvas = canvasContext.measureText(currentRowText);
let currentRowTextWidth = currentRowTextCanvas.width;
if (currentRowTextWidth > lineWidth)
{
//删去一个字符
currentRowText = text.substring(0, i + 1);
}
else
{
ellipsisText = currentRowText;
break;
}
}
return ellipsisText;
}
绘制iconfont
这里就很难蚌了,在html里面我们只需要把字体改成iconfont.css
里面的,然后用\u
+Unicode16进制就行,但是小程序这一坨不支持绘制iconfont,这个特性从canvas刚出来就有人说了,到现在都没有
所以我们直接绘制png图片吧,当然这里就需要注意了,因为图片的缩放会模糊,所以建议自己在cdn里面搞对应的尺寸
export class CreateIconFontOptionModel
{
private _canvas: Canvas = null;
/**
* 画布
*/
public get Canvas(): Canvas
{
return this._canvas;
}
public set Canvas(v: Canvas)
{
this._canvas = v;
}
private _iconUrl: string = "";
/**
* 图标链接
*/
public get IconUrl(): string
{
return this._iconUrl;
}
public set IconUrl(v: string)
{
this._iconUrl = v;
}
private _fontSize: number = 14;
/**
* 字体大小
*/
public get FontSize(): number
{
return this._fontSize;
}
public set FontSize(v: number)
{
this._fontSize = v;
}
private _fontColor: string = "#000000";
/**
* 字体颜色
*/
public get FontColor(): string
{
return this._fontColor;
}
public set FontColor(v: string)
{
this._fontColor = v;
}
private _x: number = 0;
/**
* 坐标x
*/
public get X(): number
{
return this._x;
}
public set X(v: number)
{
this._x = v;
}
private _y: number = 0;
/**
* 坐标Y
*/
public get Y(): number
{
return this._y;
}
public set Y(v: number)
{
this._y = v;
}
constructor()
{
this.Canvas = null;
this.IconUrl = "";
this.FontColor = "#000000";
this.FontSize = 14;
this.X = 0;
this.Y = 0;
}
}
还要注意这个是异步函数,要await
/**
* 绘制 iconfont 图标
* @param option
* @returns
*/
public static CreateIconFont(option: CreateIconFontOptionModel): Promise<void>
{
return new Promise((resolve, reject) =>
{
let canvas = option.Canvas;
let fontSize = option.FontSize;
let fontColor = option.FontColor;
let iconUrl = option.IconUrl;
let x = option.X;
let y = option.Y;
let fontColorRgb = ColorHelper.HexColorToRgb(fontColor);
let fontColorRed = fontColorRgb.Red;
let fontColorGreen = fontColorRgb.Green;
let fontColorBlue = fontColorRgb.Blue;
//加载图标
let iconCanvas = Canvas2DHelper.CreateOffscreenCanvas();
let iconImage = iconCanvas.createImage();
iconImage.onload = () =>
{
let iconImageHeight = iconImage.height;
let iconImageWidth = iconImage.width;
let zoomRatio = iconImageHeight / fontSize;
let iconCanvasHeight = Math.round(iconImageHeight / zoomRatio);
let iconCanvasWidth = Math.round(iconImageWidth / zoomRatio);
iconCanvas.height = iconCanvasHeight;
iconCanvas.width = iconCanvasWidth;
let iconCanvasContext = iconCanvas.getContext("2d");
iconCanvasContext.drawImage(iconImage, 0, 0, iconCanvas.width, iconCanvas.height);
let unSetColorIconData = iconCanvasContext.getImageData(0, 0, iconCanvas.width, iconCanvas.height);
let unSetColorIconPixelList = unSetColorIconData.data;
for (let i = 0; i < unSetColorIconPixelList.length; i += 4)
{
let unSetColorIconPixelRed = unSetColorIconPixelList[i];
let unSetColorIconPixelGreen = unSetColorIconPixelList[i + 1];
let unSetColorIconPixelBlue = unSetColorIconPixelList[i + 2];
let unSetColorIconPixelAlpha = unSetColorIconPixelList[i + 3];
if (0 != unSetColorIconPixelAlpha)
{
unSetColorIconPixelList[i] = fontColorRed;
unSetColorIconPixelList[i + 1] = fontColorGreen;
unSetColorIconPixelList[i + 2] = fontColorBlue;
}
}
iconCanvasContext.putImageData(unSetColorIconData, 0, 0);
let canvasContext = canvas.getContext("2d");
//绘制图标
canvasContext.drawImage(iconCanvas, x, y);
resolve("图片渲染完成");
};
iconImage.onerror = () =>
{
reject("图片加载失败");
};
iconImage.src = iconUrl;
});
}
绘制图片
这里我们需要注意图片的原点坐标和缩放模式
先搞一个缩放模式的枚举出来
export enum CreateImageMode
{
/**
* 保持长宽比,添加黑边
*/
Contain = 0,
/**
* 保持长宽比,裁切
*/
Cover = 2,
/**
* 填充,拉伸
*/
Fill = 4,
/**
* 保持原有尺寸
*/
None = 8,
}
然后再加配置
import { Canvas } from "@tarojs/taro";
import { CreateImageMode } from "./CreateImageMode";
export class CreateImageOptionModel
{
private _canvas: Canvas = null;
/**
* 画布
*/
public get Canvas(): Canvas
{
return this._canvas;
}
public set Canvas(v: Canvas)
{
this._canvas = v;
}
private _imageUrl: string = "";
/**
* 图片链接
*/
public get ImageUrl(): string
{
return this._imageUrl;
}
public set ImageUrl(v: string)
{
this._imageUrl = v;
}
private _mode: CreateImageMode = CreateImageMode.None;
/**
* 绘制图像模式
*/
public get Mode(): CreateImageMode
{
return this._mode;
}
public set Mode(v: CreateImageMode)
{
this._mode = v;
}
private _height: number = 0;
/**
* 图片高度
*/
public get Height(): number
{
return this._height;
}
public set Height(v: number)
{
this._height = v;
}
private _width: number = 0;
/**
* 图片宽度
*/
public get Width(): number
{
return this._width;
}
public set Width(v: number)
{
this._width = v;
}
private _x: number = 0;
/**
* 坐标x
*/
public get X(): number
{
return this._x;
}
public set X(v: number)
{
this._x = v;
}
private _y: number = 0;
/**
* 坐标Y
*/
public get Y(): number
{
return this._y;
}
public set Y(v: number)
{
this._y = v;
}
constructor()
{
this.Canvas = null;
this.ImageUrl = "";
this.Mode = CreateImageMode.None;
this.Height = 0;
this.Width = 0;
this.X = 0;
this.Y = 0;
}
}
最后是函数,也是异步的
/**
* 绘制图片
* @param option
* @returns
*/
public static CreateImage(option: CreateImageOptionModel): Promise<void>
{
return new Promise((resolve, reject) =>
{
let canvas = option.Canvas;
let imageUrl = option.ImageUrl;
let mode = option.Mode;
let height = option.Height;
let width = option.Width;
let x = option.X;
let y = option.Y;
let canvasWidth = canvas.width;
let canvasHeight = canvas.height;
let proportion = width / height;
let originX = Math.round(canvasWidth / 2);
let originY = Math.round(canvasHeight / 2);
let imageCanvas = Canvas2DHelper.CreateOffscreenCanvas();
let image = imageCanvas.createImage();
image.onload = () =>
{
let imageHeight = image.height;
let imageWidth = image.width;
let imageProportion = imageWidth / imageHeight;
//图片处理后大小
let createImageHeight = 0;
let createImageWidth = 0;
//图片处理后坐标变化
let createImageOriginX = 0;
let createImageOriginY = 0;
let canvasContext = canvas.getContext("2d");
switch (mode)
{
case CreateImageMode.Contain:
if (proportion >= imageProportion)
{
//以高为准
createImageHeight = height;
let heightZoomRatio = height / imageHeight;
createImageWidth = Math.round(imageWidth * heightZoomRatio);
}
else
{
//以宽为准
createImageWidth = width;
let widthZoomRatio = width / imageWidth;
createImageHeight = Math.round(imageHeight * widthZoomRatio);
}
createImageOriginX = x - Math.round(createImageWidth / 2);
createImageOriginY = y - Math.round(createImageHeight / 2);
//改变坐标系原点
canvasContext.translate(originX, originY);
//绘制图片
canvasContext.drawImage(image, createImageOriginX, createImageOriginY, createImageWidth, createImageHeight);
break;
case CreateImageMode.Cover:
let publicZoomRatio = 0;
if (proportion >= imageProportion)
{
//以高为准
createImageHeight = height;
publicZoomRatio = height / imageHeight;
createImageWidth = Math.round(imageWidth * publicZoomRatio);
}
else
{
//以宽为准
createImageWidth = width;
publicZoomRatio = width / imageWidth;
createImageHeight = Math.round(imageHeight * publicZoomRatio);
}
createImageOriginX = x - Math.round(createImageWidth / 2);
createImageOriginY = y - Math.round(createImageHeight / 2);
//改变坐标系原点
canvasContext.translate(originX, originY);
//绘制图片
canvasContext.drawImage(image, createImageOriginX, createImageOriginY, createImageWidth, createImageHeight);
break;
case CreateImageMode.Fill:
createImageHeight = height;
createImageWidth = width;
createImageOriginX = x - Math.round(createImageWidth / 2);
createImageOriginY = y - Math.round(createImageHeight / 2);
//改变坐标系原点
canvasContext.translate(originX, originY);
//绘制图片
canvasContext.drawImage(image, createImageOriginX, createImageOriginY, createImageWidth, createImageHeight);
break;
case CreateImageMode.None:
default:
createImageHeight = imageHeight;
createImageWidth = imageWidth;
createImageOriginX = x - Math.round(createImageWidth / 2);
createImageOriginY = y - Math.round(createImageHeight / 2);
//改变坐标系原点
canvasContext.translate(originX, originY);
//绘制图片
canvasContext.drawImage(image, createImageOriginX, createImageOriginY, createImageWidth, createImageHeight);
break;
}
resolve("图片渲染完成");
};
image.onerror = () =>
{
reject("图片加载失败");
};
image.src = imageUrl;
});
}
转为base64图片
这个还是蛮常用的,毕竟十六进制比二进制短
/**
* 转换为图片
* @param canvas
* @returns
*/
public static ConvertToImage(canvas: Canvas): string
{
// let canvasContext = canvas.getContext("2d");
let imageUrl = canvas.toDataURL();
return imageUrl;
}
基于路径绘图
这个是自定义操作步骤,所以我们需要封装几个类
首先是声明一个Point类,这个是就是坐标
export class Point
{
private _x: number = 0;
/**
* 坐标x
*/
public get X(): number
{
return this._x;
}
public set X(v: number)
{
this._x = v;
}
private _y: number = 0;
/**
* 坐标y
*/
public get Y(): number
{
return this._y;
}
public set Y(v: number)
{
this._y = v;
}
constructor(x: number = 0, y: number = 0)
{
this.X = x;
this.Y = y;
}
}
然后声明一个枚举,用来确定操作
export enum PathType
{
/**
* 点
*/
Point = 0,
/**
* 直线
*/
Line = 2,
/**
* 圆角
*/
Round = 4,
}
声明一个基类,所有类型的操作都继承这个类
export class Path
{
private _pathType: PathType = PathType.Point;
/**
* 路径类型
*/
protected get PathType(): PathType
{
return this._pathType;
}
protected set PathType(v: PathType)
{
this._pathType = v;
}
}
将枚举类型对应声明一个类
点操作类
export class PointPath extends Path
{
private _point: Point = null;
/**
* 坐标
*/
public get Point(): Point
{
return this._point;
}
public set Point(v: Point)
{
this._point = v;
}
constructor()
{
super();
this.PathType = PathType.Point;
this.Point = null;
}
}
直线操作类
export class LinePath extends Path
{
private _point: Point = null;
/**
* 坐标
*/
public get Point(): Point
{
return this._point;
}
public set Point(v: Point)
{
this._point = v;
}
constructor()
{
super();
this.PathType = PathType.Line;
this.Point = null;
}
}
圆角操作类
export class RoundPath extends Path
{
private _startPoint: Point = null;
/**
* 起始点
*/
public get StartPoint(): Point
{
return this._startPoint;
}
public set StartPoint(v: Point)
{
this._startPoint = v;
}
private _endPoint: Point = null;
/**
* 截止点
*/
public get EndPoint(): Point
{
return this._endPoint;
}
public set EndPoint(v: Point)
{
this._endPoint = v;
}
private _radius: number = 0;
/**
* 圆角半径
*/
public get Radius(): number
{
return this._radius;
}
public set Radius(v: number)
{
this._radius = v;
}
/**
*
*/
constructor()
{
super();
this.PathType = PathType.Round;
this.StartPoint = null;
this.EndPoint = null;
this.Radius = 0;
}
}
然后就是配置类
export class CreateGraphOptionModel
{
private _canvas: Canvas = null;
/**
* 画布
*/
public get Canvas(): Canvas
{
return this._canvas;
}
public set Canvas(v: Canvas)
{
this._canvas = v;
}
private _pathList: Array<Path> = [];
/**
* 路径集合
*/
public get pathList(): Array<Path>
{
return this._pathList;
}
public set pathList(v: Array<Path>)
{
this._pathList = v;
}
constructor()
{
this.Canvas = null;
this.pathList = [];
}
}
对应的函数
/**
* 绘制图形
* @param option
*/
public static CreateGraph(option: CreateGraphOptionModel): void
{
let canvas = option.Canvas;
let pathList = option.pathList;
if (false == NullHelper.IsNullOrUndefined(pathList) && pathList.length > 0)
{
let canvasContext = canvas.getContext("2d");
//开始路径
canvasContext.beginPath();
for (let i = 0; i < pathList.length; i++)
{
let pathListItem = pathList[i];
let pathListItemType = pathListItem.PathType;
switch (pathListItemType)
{
case PathType.Line:
let linePath = pathListItem as LinePath;
if (false == NullHelper.IsNullOrUndefined(linePath))
{
canvasContext.lineTo(linePath.Point.X, linePath.Point.Y);
}
break;
case PathType.Round:
let roundPath = pathListItem as RoundPath;
if (false == NullHelper.IsNullOrUndefined(roundPath))
{
let startPoint = roundPath.StartPoint;
let endPoint = roundPath.EndPoint;
let radius = roundPath.Radius;
canvasContext.arcTo(startPoint.X, startPoint.Y, endPoint.X, endPoint.Y, radius);
}
break;
case PathType.Point:
default:
let pointPath = pathListItem as PointPath;
if (false == NullHelper.IsNullOrUndefined(pointPath))
{
canvasContext.moveTo(pointPath.Point.X, pointPath.Point.Y);
}
break;
}
}
//合并路径
canvasContext.closePath();
}
}
描边
export class CreateGraphStrokeColorOptionModel extends CreateGraphOptionModel
{
private _strokeColor: string = "#000000";
/**
* 描边颜色
*/
public get StrokeColor(): string
{
return this._strokeColor;
}
public set StrokeColor(v: string)
{
this._strokeColor = v;
}
private _lineWidth: number = 1;
/**
* 描边宽度
*/
public get LineWidth(): number
{
return this._lineWidth;
}
public set LineWidth(v: number)
{
this._lineWidth = v;
}
constructor()
{
super();
this.StrokeColor = "#000000";
this.LineWidth = 1;
}
}
/**
* 绘制图形,描边
* @param option
*/
public static CreateGraphStrokeColor(option: CreateGraphStrokeColorOptionModel): void
{
let canvas = option.Canvas;
let pathList = option.pathList;
let strokeColor = option.StrokeColor;
let lineWidth = option.LineWidth;
//绘制图形
Canvas2DHelper.CreateGraph(option);
let canvasContext = canvas.getContext("2d");
//描边宽度
canvasContext.lineWidth = lineWidth;
//选择描边颜色
canvasContext.strokeStyle = color;
//描边
canvasContext.stroke();
}
填充颜色
export class CreateGraphFillBackgroundColorOptionModel extends CreateGraphOptionModel
{
private _backgroundColor: string = "#000000";
/**
* 描边颜色
*/
public get BackgroundColor(): string
{
return this._backgroundColor;
}
public set BackgroundColor(v: string)
{
this._backgroundColor = v;
}
constructor()
{
super();
this.BackgroundColor = "#000000";
}
}
/**
* 绘制图形,填充颜色
* @param option
*/
public static CreateGraphFillBackgroundColor(option: CreateGraphFillBackgroundColorOptionModel): void
{
let canvas = option.Canvas;
let pathList = option.pathList;
let backgroundColor = option.BackgroundColor;
//绘制图形
Canvas2DHelper.CreateGraph(option);
let canvasContext = canvas.getContext("2d");
//选择背景色
canvasContext.fillStyle = backgroundColor;
//填充背景色
canvasContext.fill();
}