文章目录
1、创建海报的基本逻辑
- 1、先创建dom元素
wrapperHeight
是根据海报的内容计算出来海报的高度
js
复制代码
<view class="preview-card-wrap" @tap.stop>
<view class="poster-box" :class="{ show: isDrew }">
<canvas
id="myCanvas"
canvas-id="myCanvas"
:style="{
width: '750rpx',
height: wrapperHeight + 'rpx',
}"
></canvas>
</view>
</view>
js
复制代码
class Poster {
canvasId: string;
instanceComponent: ReturnType<typeof getCurrentInstance> | undefined;
ctx: UniApp.CanvasContext | undefined = undefined;
width = 0;
height = 0;
isPixel: boolean;
drawSteps: DrawSteps = [];
constructor(
canvasId: string,
instance?: ReturnType<typeof getCurrentInstance>,
{ isPixel = true } = <InitConfig>{}
) {
this.canvasId = canvasId;
this.instanceComponent = instance;
this.ctx = uni.createCanvasContext(canvasId, instance);
this.isPixel = isPixel;
}
/**
* 设置画布大小
* @param width 画布宽度
* @param height 画布高度
*/
public setCanvasSize(width: number, height: number) {
this.width = width;
this.height = height;
}
/**
* 清空画布
*/
clearAll() {
this.ctx?.clearRect(
0,
0,
this.getPixel(this.width),
this.getPixel(this.height)
);
// clearRect() 方法只是将指定区域的像素设置为透明,但不会直接触发画布的重新绘制。
// 因此,需要在清除画布后显式调用 draw() 方法,才能使清除的效果立即生效,即将空白画布展示在页面上。
// 这个方法只是为了更新画布
this.ctx?.draw();
}
}
export default Poster;
js
复制代码
import { getCurrentInstance, ref } from "vue";
const poster = ref<Poster | undefined | any>(undefined);
poster.value = new Poster("myCanvas", self, { isPixel: false });
const self = getCurrentInstance();
// 设置画布大小
poster.value.setCanvasSize(750, wrapperHeight.value);
// 绘制前先清空画布
poster.value.clearAll();
2、用canvas绘制文字
js
复制代码
/**
* 获取大小
* @param size
*/
getPixel(size: number) {
return this.isPixel ? size : rpx2px(size);
}
js
复制代码
/**
* 获取文字宽度
* @param text 文字
* @param fontStyle 字体样式,同 css 的 font 属性
* @returns {number} 文字宽度
*/
public getTextWidth(text: string, fontStyle?: string) {
if (!this.ctx || !text.trim()) return 0;
this.ctx.save();
this.ctx.font = fontStyle || "14px sans-serif";
const dimension = this.ctx.measureText(text);
this.ctx.restore();
return this.isPixel ? dimension.width : px2rpx(dimension.width);
}
js
复制代码
// 给文字加上省略号
public correctEllipsisText(text: string, width: number, fontStyle?: string) {
let resultText = "";
const strSplits = text.split("");
while (strSplits.length > 0) {
const s = strSplits.shift();
const isGtWidth =
this.getPixel(this.getTextWidth(resultText + s, fontStyle)) >
this.getPixel(width);
if (isGtWidth) {
resultText = resultText.substring(0, resultText.length) + "...";
break;
}
resultText += s;
}
return resultText;
}
js
复制代码
/**
* 绘制文字
* @param text 文字
* @param x 横向绘制点
* @param y 纵向绘制点
* @param maxWidth 绘制区域最大宽度,文字宽度超过则换行
* @param color 文字颜色
* @param fontSize 文字大小
* @param fontWeight 文字粗细
* @param borderWidth 文字描边粗细
* @param borderColor 文字描边样式
* @param lineHeight 行高,即文字行与行之间的间距
* @param UseEllipsis 当超出文字宽度是否使用省略号
*/
public async drawText({
text,
x,
y,
maxWidth,
color,
fontSize,
fontFamily,
fontWeight = 500,
borderWidth,
borderColor,
lineHeight = 1.2,
UseEllipsis = true,
}: TextDrawStep) {
if (!this.ctx) return;
const fontStyle = `${fontWeight} ${
fontSize ? this.getPixel(fontSize) : 14
}px ${`${fontFamily}` || "sans-serif"}`;
this.ctx.save();
this.ctx.setTextBaseline("top");
this.ctx.font = fontStyle;
color && (this.ctx.fillStyle = color);
if (borderColor) this.ctx.strokeStyle = borderColor;
// 绘制文字边框样式
if (borderWidth) {
this.ctx.lineWidth = borderWidth;
}
if (UseEllipsis) {
// 将超出canvas宽度的文字用...展示
const drawText = this.correctEllipsisText(
text,
maxWidth || this.width,
fontStyle
);
if (borderWidth) {
this.ctx.strokeText(
drawText,
this.getPixel(x),
this.getPixel(y),
maxWidth ? this.getPixel(maxWidth) : maxWidth
);
}
this.ctx.fillText(
drawText,
this.getPixel(x),
this.getPixel(y),
maxWidth ? this.getPixel(maxWidth) : maxWidth
);
} else {
// 将文本分割成数组
const words = text.split("");
let line = ""; // 当前行的文字内容
let yPos = y; // 当前行的纵坐标位置
for (let i = 0; i < words.length; i++) {
const testLine = line + words[i]; // 当前行加上当前单词
// 测量当前行的宽度
const textWidth = this.getTextWidth(testLine, fontStyle);
// 如果当前行的宽度超过最大的宽度 需要换行
if (textWidth > this.getPixel(maxWidth || this.width)) {
// 讲当前行绘制到canvas上
if (borderWidth) {
this.ctx.strokeText(
line,
this.getPixel(x),
this.getPixel(y),
maxWidth ? this.getPixel(maxWidth) : maxWidth
);
this.ctx.fillText(
line,
this.getPixel(x),
this.getPixel(yPos),
maxWidth ? this.getPixel(maxWidth) : maxWidth
);
// 开始新一行
yPos += lineHeight * (fontSize ? this.getPixel(fontSize) : 14);
} else {
line = testLine;
}
}
}
this.ctx.strokeText(line, this.getPixel(x), this.getPixel(y));
this.ctx.fillText(line, this.getPixel(x), this.getPixel(yPos));
}
this.ctx.restore();
}
3、绘制矩形
js
复制代码
/**
* 绘制直边形状
* @param lines 坐标数组第一个为设置落笔点,坐标数组最后一个如果和第一个一样可省略
* @param fillColor 填充颜色
*/
public drawLineShape({ lines, fillColor }: LineShapeDrawStep) {
if (!this.ctx || !lines.length) return;
this.ctx.save();
// 开始绘制路径
this.ctx.beginPath();
const [x, y] = lines[0];
this.ctx.moveTo(this.getPixel(x), this.getPixel(y));
// 遍历坐标数组,绘制直线段
for (let i = 1; i < lines.length; i++) {
const [ix, iy] = lines[i];
this.ctx.lineTo(this.getPixel(ix), this.getPixel(iy));
}
if (this.ctx && fillColor) {
this.ctx.fillStyle = fillColor;
this.ctx.fill();
} else {
this.ctx.closePath();
}
this.ctx.restore();
}
4、绘制圆形
js
复制代码
/**
* 绘制圆形
*/
public drawCircleShape({
x,
y,
radius,
startAngle,
endAngle,
anticlockwise,
fillColor,
}: CircleShapeDrawStep) {
if (!this.ctx) return;
this.ctx.save();
this.ctx.beginPath();
this.ctx.arc(
this.getPixel(x),
this.getPixel(y),
this.getPixel(radius),
this.getPixel(startAngle),
this.getPixel(endAngle),
anticlockwise
);
if (this.ctx && fillColor) {
this.ctx.setFillStyle(fillColor);
this.ctx.fill();
} else {
this.ctx.closePath();
}
this.ctx.restore();
}
5、绘制圆角矩形
js
复制代码
/**
* 绘制圆角矩形
* @param x x坐标
* @param y y坐标
* @param width 宽度
* @param height 高度
* @param radius 圆角半径
* @param fillColor 填充颜色
*/
public roundRect = ({
x,
y,
width,
height,
radius,
fillColor,
}: roundRectShapeDrawStep) => {
if (!this.ctx) return;
const dx = this.getPixel(x);
const dy = this.getPixel(y);
const dRadius = this.getPixel(radius);
const dWidth = this.getPixel(width);
const dHeight = this.getPixel(height);
this.ctx.beginPath();
this.ctx.moveTo(dx + dRadius, dy);
// 下面三个点形成切线来画出圆弧
this.ctx.lineTo(dx + dWidth - dRadius, dy);
this.ctx.arcTo(dx + dWidth, dy, dx + dWidth, dy + dRadius, dRadius);
this.ctx.lineTo(dx + dWidth, dy + dHeight - dRadius);
this.ctx.arcTo(
dx + dWidth,
dy + dHeight,
dx + dWidth - dRadius,
dy + dHeight,
dRadius
);
this.ctx.lineTo(dx + dRadius, dy + dHeight);
this.ctx.arcTo(dx, dy + dHeight, dx, dy + dHeight - dRadius, dRadius);
this.ctx.lineTo(dx, dy + dRadius);
this.ctx.arcTo(dx, dy, dx + dRadius, dy, dRadius);
this.ctx.closePath();
this.ctx.fillStyle = fillColor;
this.ctx.fill();
};
6、绘制图片
js
复制代码
/**
* 绘制图片
* @param image 图片路径,必须为本地路径或临时路径
* @param x 横向绘制点
* @param y 纵向绘制点
* @param width 绘制宽度
* @param height 绘制高度
* @param isCircle 是否为圆形图, 宽高需相等
*/
public async drawImage({
image,
x,
y,
width,
height,
isCircle = false,
clipConfig,
}: ImageDrawStep) {
if (!this.ctx) return;
this.ctx.save();
if (isCircle) {
const r = Math.floor(this.getPixel(width) / 2);
// context.arc(x, y, radius, startAngle, endAngle, anticlockwise);
this.ctx.arc(
this.getPixel(x) + r,
this.getPixel(y) + r,
r,
0,
2 * Math.PI
);
// 只有在剪切路径范围内的内容才会被保留,超出剪切路径范围的部分会被裁剪掉。
this.ctx.clip();
}
await sleep(50);
let clipParams: number[] = [];
if (clipConfig) {
clipParams = [
clipConfig.x,
clipConfig.y,
clipConfig.width,
clipConfig.height,
];
}
this.ctx.drawImage(
image,
...clipParams,
this.getPixel(x),
this.getPixel(y),
this.getPixel(width),
this.getPixel(height)
);
// console.log(this.ctx, "查看图片的上下文");
this.ctx.restore();
}
7、执行绘制
js
复制代码
/**
* 执行绘制
*/
async draw(callback?: Function) {
// 初始化绘图步骤数组的索引
let index = 0;
// 循环遍历绘图步骤数组
while (index < this.drawSteps.length) {
// 从当前绘图步骤中提取出类型和其他属性
const { type, ...otherProps } = <DrawStep>this.drawSteps[index];
const stepProps = <AnyObject>otherProps;
const props = <AnyObject>{};
// 合并其他属性到 props 中
Object.assign(
props,
stepProps.getProps
? await stepProps.getProps(this.drawSteps, index)
: stepProps
);
// this.drawSteps[index].drawData = {
// ...props,
// ...(this.drawSteps[index].drawData || {}),
// };
// 根据类型执行相应的绘图操作
if (type === DrawType.Text) {
await this.drawText(<TextDrawStep>props);
} else if (type === DrawType.Image) {
await this.drawImage(<ImageDrawStep>props);
} else if (type === DrawType.LineShape) {
await this.drawLineShape(<LineShapeDrawStep>props);
} else if (type === DrawType.CircleShape) {
await this.drawCircleShape(<CircleShapeDrawStep>props);
} else if (type === DrawType.RoundRectShape) {
await this.roundRect(<roundRectShapeDrawStep>props);
}
// 如果当前绘图步骤需要立即绘制,则执行绘图同步操作
props.immediateDraw && (await this.syncDraw());
// 移动到下一个绘图步骤
index += 1;
}
// 执行最终的绘图操作,并在绘制完成后执行回调函数
this.ctx?.draw(true, (res) => {
callback?.(res);
});
}
8、完整的代码
js
复制代码
import type { getCurrentInstance } from "vue";
import { px2rpx, rpx2px } from "@/utils/view";
import type {
DrawStep,
DrawSteps,
ImageDrawStep,
InitConfig,
LineShapeDrawStep,
TextDrawStep,
CircleShapeDrawStep,
roundRectShapeDrawStep,
} from "./poster";
import { DrawType } from "./poster";
import { sleep } from "@/utils";
class Poster {
canvasId: string;
instanceComponent: ReturnType<typeof getCurrentInstance> | undefined;
ctx: UniApp.CanvasContext | undefined = undefined;
width = 0;
height = 0;
isPixel: boolean;
drawSteps: DrawSteps = [];
constructor(
canvasId: string,
instance?: ReturnType<typeof getCurrentInstance>,
{ isPixel = true } = <InitConfig>{}
) {
this.canvasId = canvasId;
this.instanceComponent = instance;
this.ctx = uni.createCanvasContext(canvasId, instance);
this.isPixel = isPixel;
}
/**
* 获取大小
* @param size
*/
getPixel(size: number) {
return this.isPixel ? size : rpx2px(size);
}
public getCanvasSize() {
return {
width: this.width,
height: this.height,
};
}
/**
* 设置画布大小
* @param width 画布宽度
* @param height 画布高度
*/
public setCanvasSize(width: number, height: number) {
this.width = width;
this.height = height;
}
/**
* 获取文字宽度
* @param text 文字
* @param fontStyle 字体样式,同 css 的 font 属性
* @returns {number} 文字宽度
*/
public getTextWidth(text: string, fontStyle?: string) {
if (!this.ctx || !text.trim()) return 0;
this.ctx.save();
this.ctx.font = fontStyle || "14px sans-serif";
const dimension = this.ctx.measureText(text);
this.ctx.restore();
return this.isPixel ? dimension.width : px2rpx(dimension.width);
}
// 给文字加上省略号
public correctEllipsisText(text: string, width: number, fontStyle?: string) {
let resultText = "";
const strSplits = text.split("");
while (strSplits.length > 0) {
const s = strSplits.shift();
const isGtWidth =
this.getPixel(this.getTextWidth(resultText + s, fontStyle)) >
this.getPixel(width);
if (isGtWidth) {
resultText = resultText.substring(0, resultText.length) + "...";
break;
}
resultText += s;
}
return resultText;
}
/**
* 绘制图片
* @param image 图片路径,必须为本地路径或临时路径
* @param x 横向绘制点
* @param y 纵向绘制点
* @param width 绘制宽度
* @param height 绘制高度
* @param isCircle 是否为圆形图, 宽高需相等
*/
public async drawImage({
image,
x,
y,
width,
height,
isCircle = false,
clipConfig,
}: ImageDrawStep) {
if (!this.ctx) return;
this.ctx.save();
if (isCircle) {
const r = Math.floor(this.getPixel(width) / 2);
// context.arc(x, y, radius, startAngle, endAngle, anticlockwise);
this.ctx.arc(
this.getPixel(x) + r,
this.getPixel(y) + r,
r,
0,
2 * Math.PI
);
// 只有在剪切路径范围内的内容才会被保留,超出剪切路径范围的部分会被裁剪掉。
this.ctx.clip();
}
await sleep(50);
let clipParams: number[] = [];
if (clipConfig) {
clipParams = [
clipConfig.x,
clipConfig.y,
clipConfig.width,
clipConfig.height,
];
}
this.ctx.drawImage(
image,
...clipParams,
this.getPixel(x),
this.getPixel(y),
this.getPixel(width),
this.getPixel(height)
);
// console.log(this.ctx, "查看图片的上下文");
this.ctx.restore();
}
/**
* 绘制文字
* @param text 文字
* @param x 横向绘制点
* @param y 纵向绘制点
* @param maxWidth 绘制区域最大宽度,文字宽度超过则换行
* @param color 文字颜色
* @param fontSize 文字大小
* @param fontWeight 文字粗细
* @param borderWidth 文字描边粗细
* @param borderColor 文字描边样式
* @param lineHeight 行高,即文字行与行之间的间距
* @param UseEllipsis 当超出文字宽度是否使用省略号
*/
public async drawText({
text,
x,
y,
maxWidth,
color,
fontSize,
fontFamily,
fontWeight = 500,
borderWidth,
borderColor,
lineHeight = 1.2,
UseEllipsis = true,
}: TextDrawStep) {
if (!this.ctx) return;
const fontStyle = `${fontWeight} ${
fontSize ? this.getPixel(fontSize) : 14
}px ${`${fontFamily}` || "sans-serif"}`;
this.ctx.save();
this.ctx.setTextBaseline("top");
this.ctx.font = fontStyle;
color && (this.ctx.fillStyle = color);
if (borderColor) this.ctx.strokeStyle = borderColor;
// 绘制文字边框样式
if (borderWidth) {
this.ctx.lineWidth = borderWidth;
}
if (UseEllipsis) {
// 将超出canvas宽度的文字用...展示
const drawText = this.correctEllipsisText(
text,
maxWidth || this.width,
fontStyle
);
if (borderWidth) {
this.ctx.strokeText(
drawText,
this.getPixel(x),
this.getPixel(y),
maxWidth ? this.getPixel(maxWidth) : maxWidth
);
}
this.ctx.fillText(
drawText,
this.getPixel(x),
this.getPixel(y),
maxWidth ? this.getPixel(maxWidth) : maxWidth
);
} else {
// 将文本分割成数组
const words = text.split("");
let line = ""; // 当前行的文字内容
let yPos = y; // 当前行的纵坐标位置
for (let i = 0; i < words.length; i++) {
const testLine = line + words[i]; // 当前行加上当前单词
// 测量当前行的宽度
const textWidth = this.getTextWidth(testLine, fontStyle);
// 如果当前行的宽度超过最大的宽度 需要换行
if (textWidth > this.getPixel(maxWidth || this.width)) {
// 讲当前行绘制到canvas上
if (borderWidth) {
this.ctx.strokeText(
line,
this.getPixel(x),
this.getPixel(y),
maxWidth ? this.getPixel(maxWidth) : maxWidth
);
this.ctx.fillText(
line,
this.getPixel(x),
this.getPixel(yPos),
maxWidth ? this.getPixel(maxWidth) : maxWidth
);
// 开始新一行
yPos += lineHeight * (fontSize ? this.getPixel(fontSize) : 14);
} else {
line = testLine;
}
}
}
this.ctx.strokeText(line, this.getPixel(x), this.getPixel(y));
this.ctx.fillText(line, this.getPixel(x), this.getPixel(yPos));
}
this.ctx.restore();
}
/**
* 绘制直边形状
* @param lines 坐标数组第一个为设置落笔点,坐标数组最后一个如果和第一个一样可省略
* @param fillColor 填充颜色
*/
public drawLineShape({ lines, fillColor, gradients }: LineShapeDrawStep) {
if (!this.ctx || !lines.length) return;
this.ctx.save();
this.ctx.beginPath();
const [x, y] = lines[0];
this.ctx.moveTo(this.getPixel(x), this.getPixel(y));
for (let i = 1; i < lines.length; i++) {
const [ix, iy] = lines[i];
this.ctx.lineTo(this.getPixel(ix), this.getPixel(iy));
}
if (this.ctx && fillColor) {
this.ctx.fillStyle = fillColor;
this.ctx.fill();
} else if (this.ctx && gradients?.length) {
var lineargradient = this.ctx.createLinearGradient(
gradients[0],
gradients[1],
gradients[2],
gradients[3]
);
lineargradient.addColorStop(0, "#363636");
lineargradient.addColorStop(1, "white");
this.ctx.setFillStyle(lineargradient);
this.ctx.fill();
} else {
this.ctx.closePath();
}
this.ctx.restore();
}
/**
* 绘制圆形
*/
public drawCircleShape({
x,
y,
radius,
startAngle,
endAngle,
anticlockwise,
fillColor,
}: CircleShapeDrawStep) {
if (!this.ctx) return;
this.ctx.save();
this.ctx.beginPath();
this.ctx.arc(
this.getPixel(x),
this.getPixel(y),
this.getPixel(radius),
this.getPixel(startAngle),
this.getPixel(endAngle),
anticlockwise
);
if (this.ctx && fillColor) {
this.ctx.setFillStyle(fillColor);
this.ctx.fill();
} else {
this.ctx.closePath();
}
this.ctx.restore();
}
syncDraw(): Promise<void> {
return new Promise((resolve) => {
this.ctx?.draw(true, async () => {
await sleep(30);
resolve();
});
});
}
/**
* 绘制圆角矩形
* @param x x坐标
* @param y y坐标
* @param width 宽度
* @param height 高度
* @param radius 圆角半径
* @param fillColor 填充颜色
*/
public roundRect = ({
x,
y,
width,
height,
radius,
fillColor,
}: roundRectShapeDrawStep) => {
if (!this.ctx) return;
const dx = this.getPixel(x);
const dy = this.getPixel(y);
const dRadius = this.getPixel(radius);
const dWidth = this.getPixel(width);
const dHeight = this.getPixel(height);
this.ctx.beginPath();
this.ctx.moveTo(dx + dRadius, dy);
// 下面三个点形成切线来画出圆弧
this.ctx.lineTo(dx + dWidth - dRadius, dy);
this.ctx.arcTo(dx + dWidth, dy, dx + dWidth, dy + dRadius, dRadius);
this.ctx.lineTo(dx + dWidth, dy + dHeight - dRadius);
this.ctx.arcTo(
dx + dWidth,
dy + dHeight,
dx + dWidth - dRadius,
dy + dHeight,
dRadius
);
this.ctx.lineTo(dx + dRadius, dy + dHeight);
this.ctx.arcTo(dx, dy + dHeight, dx, dy + dHeight - dRadius, dRadius);
this.ctx.lineTo(dx, dy + dRadius);
this.ctx.arcTo(dx, dy, dx + dRadius, dy, dRadius);
this.ctx.closePath();
this.ctx.fillStyle = fillColor;
this.ctx.fill();
};
/**
* 执行绘制
*/
async draw(callback?: Function) {
// 初始化绘图步骤数组的索引
let index = 0;
// 循环遍历绘图步骤数组
while (index < this.drawSteps.length) {
// 从当前绘图步骤中提取出类型和其他属性
const { type, ...otherProps } = <DrawStep>this.drawSteps[index];
const stepProps = <AnyObject>otherProps;
const props = <AnyObject>{};
// 合并其他属性到 props 中
Object.assign(
props,
stepProps.getProps
? await stepProps.getProps(this.drawSteps, index)
: stepProps
);
// this.drawSteps[index].drawData = {
// ...props,
// ...(this.drawSteps[index].drawData || {}),
// };
// 根据类型执行相应的绘图操作
if (type === DrawType.Text) {
await this.drawText(<TextDrawStep>props);
} else if (type === DrawType.Image) {
await this.drawImage(<ImageDrawStep>props);
} else if (type === DrawType.LineShape) {
await this.drawLineShape(<LineShapeDrawStep>props);
} else if (type === DrawType.CircleShape) {
await this.drawCircleShape(<CircleShapeDrawStep>props);
} else if (type === DrawType.RoundRectShape) {
await this.roundRect(<roundRectShapeDrawStep>props);
}
// 如果当前绘图步骤需要立即绘制,则执行绘图同步操作
props.immediateDraw && (await this.syncDraw());
// 移动到下一个绘图步骤
index += 1;
}
// 执行最终的绘图操作,并在绘制完成后执行回调函数
this.ctx?.draw(true, (res) => {
callback?.(res);
});
}
/**
* 将绘制内容转成图片
* @returns {Promise<string>} 图片临时路径
*/
canvas2Image(): Promise<UniApp.CanvasToTempFilePathRes> | undefined {
if (!this.ctx) return;
return new Promise((resolve) => {
uni.canvasToTempFilePath(
{
canvasId: this.canvasId,
x: 0,
y: 0,
width: this.width,
height: this.height,
success: resolve,
},
this.instanceComponent
);
});
}
/**
* 清空画布
*/
clearAll() {
this.ctx?.clearRect(
0,
0,
this.getPixel(this.width),
this.getPixel(this.height)
);
// clearRect() 方法只是将指定区域的像素设置为透明,但不会直接触发画布的重新绘制。
// 因此,需要在清除画布后显式调用 draw() 方法,才能使清除的效果立即生效,即将空白画布展示在页面上。
// 这个方法只是为了更新画布
this.ctx?.draw();
}
}
export default Poster;
js
复制代码
export enum DrawType {
Text = "text",
Image = "image",
LineShape = "lineShape",
CircleShape = "circleShape",
RoundRectShape = "roundRectShape",
}
export interface InitConfig {
isPixel: boolean;
}
export type BaseDrawStep = {
x: number;
y: number;
};
export type GetProps<O> = {
getProps: (steps: DrawSteps, index: number) => O | Promise<O>;
};
export type TextDrawStep = BaseDrawStep & {
text: string;
maxWidth?: number;
color?: string;
fontSize?: number;
fontFamily?: string;
fontWeight?: number;
borderWidth?: number;
borderColor?: string;
lineHeight?: number;
UseEllipsis?: boolean;
};
export type ImageDrawStep = BaseDrawStep & {
image: string;
width: number;
height: number;
isCircle?: boolean;
clipConfig?: {
x: number;
y: number;
width: number;
height: number;
};
};
export type LineShapeDrawStep = {
lines: Array<[number, number]>;
fillColor?: string;
strokeStyle?: string;
gradients?: Array<number>;
};
export type CircleShapeDrawStep = {
x: number;
y: number;
radius: number;
startAngle: number;
endAngle: number;
anticlockwise: boolean;
fillColor?: string;
};
export type roundRectShapeDrawStep = {
ctx: CanvasRenderingContext2D;
x: number;
y: number;
width: number;
height: number;
radius: number;
fillColor: any;
};
export type DrawStep =
| ({ type: DrawType.Text } & (TextDrawStep | GetProps<TextDrawStep>))
| ({ type: DrawType.Image } & (ImageDrawStep | GetProps<ImageDrawStep>))
| ({ type: DrawType.LineShape } & (
| LineShapeDrawStep
| GetProps<LineShapeDrawStep>
));
export type DrawSteps = Array<
DrawStep & { drawData?: AnyObject; immediateDraw?: boolean }
>;
js
复制代码
export const px2rpx = (px: number) => px / (uni.upx2px(100) / 100);
export const rpx2px = (rpx: number) => uni.upx2px(rpx);
js
复制代码
import { getCurrentInstance, ref } from "vue";
const poster = ref<Poster | undefined | any>(undefined);
poster.value = new Poster("myCanvas", self, { isPixel: false });
const self = getCurrentInstance();
// 设置画布大小
poster.value.setCanvasSize(750, wrapperHeight.value);
// 绘制前先清空画布
poster.value.clearAll();
poster.value.drawSteps = [
{
// 背景高斯模糊图片
type: DrawType.Image,
getProps: (steps: DrawSteps) => {
return {
image: flurBg.path,
x: 0,
y: 0,
width: 750,
height: 308,
};
},
},
];
await poster.value.draw((res: any) => {
uni.hideLoading();
});