小程序海報生成海報【vue】

文章目錄

    • 1、創建海報的基本邏輯
    • 2、用canvas繪制文字
    • 3、繪制矩形
    • 4、繪制圓形
    • 5、繪制圓角矩形
    • 6、繪制圖片
    • 7、執行繪制
    • 8、完整的代碼

1、創建海報的基本邏輯

  • 1、先創建dom元素
    wrapperHeight是根據海報的內容計算出來海報的高度
    <view class="preview-card-wrap" @tap.stop><view class="poster-box" :class="{ show: isDrew }"><canvasid="myCanvas"canvas-id="myCanvas":style="{width: '750rpx',height: wrapperHeight + 'rpx',}"></canvas></view></view>
  • 2、創建海報類
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;
  • 3、創建海報類
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繪制文字

  • 先將px和rpx進行轉換
  /*** 獲取大小* @param size*/getPixel(size: number) {return this.isPixel ? size : rpx2px(size);}
  • 獲取文字的寬度
  /*** 獲取文字寬度* @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 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、繪制矩形

  /*** 繪制直邊形狀* @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、繪制圓形

  /*** 繪制圓形*/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、繪制圓角矩形

  /*** 繪制圓角矩形* @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、繪制圖片

  /*** 繪制圖片* @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、執行繪制

 /*** 執行繪制*/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、完整的代碼

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;
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 }
>;
export const px2rpx = (px: number) => px / (uni.upx2px(100) / 100);
export const rpx2px = (rpx: number) => uni.upx2px(rpx);
  • 使用

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();});

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/719432.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/719432.shtml
英文地址,請注明出處:http://en.pswp.cn/news/719432.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

支持向量機 SVM | 線性可分:硬間隔模型公式推導

目錄 一. SVM的優越性二. SVM算法推導小節概念 在開始講述SVM算法之前&#xff0c;我們先來看一段定義&#xff1a; 支持向量機(Support VecorMachine, SVM)本身是一個二元分類算法&#xff0c;支持線性分類和非線性分類的分類應用&#xff0c;同時通過OvR或者OvO的方式可以應用…

長貴對趙本山說:你需要我們家大腳,我立馬給你配雙大鞋!

長貴對趙本山說&#xff1a;你需要我們家大腳&#xff0c;我立馬給你配雙大鞋&#xff01; --小品《鄉村愛情》&#xff08;中2&#xff09;的臺詞 表演者&#xff1a;趙本山 于月仙 王小利 唐鑒軍等 &#xff08;接上&#xff09; 哈哈哈 伊拉克啊 這地方也不產這玩意吧 …

Chat GPT:AI聊天機器人的革命性突破!

一、引言 近年來&#xff0c;人工智能&#xff08;AI&#xff09;技術的發展日新月異&#xff0c;其中最具代表性的成果之一便是Chat GPT。這款基于自然語言處理&#xff08;NLP&#xff09;技術的聊天機器人&#xff0c;以其高度智能、靈活多變的特點&#xff0c;迅速吸引了全…

筆記74:在SLAM建圖過程中,為什么要使用【障礙物點云配準算法】和【里程計估算算法】結合的方法

僅使用【障礙物點云配準算法】&#xff0c;很容易導致在一條長通道中&#xff0c;因為前后兩幀的雷達點云圖過于相似&#xff0c;導致特征匹配一直完全重合&#xff0c;使得機器人建圖一直停留在原地&#xff0c;但實體機器人早就沿著通道跑向遠端了&#xff1b; 使用Hector_ma…

(學習日記)2024.03.02:UCOSIII第四節:創建任務

寫在前面&#xff1a; 由于時間的不足與學習的碎片化&#xff0c;寫博客變得有些奢侈。 但是對于記錄學習&#xff08;忘了以后能快速復習&#xff09;的渴望一天天變得強烈。 既然如此 不如以天為單位&#xff0c;以時間為順序&#xff0c;僅僅將博客當做一個知識學習的目錄&a…

js 精確計算(解決js四則運算精度缺失問題)

js的小數的運算&#xff0c;可能會得到一個不精確的結果&#xff0c;因為所有的運算都要轉換成二進制去計算&#xff0c;然而&#xff0c;二進制無法精確表示1/10。 var a 0.1 0.2; console.log(a); //打印結果&#xff1a;0.30000000000000004因此需要使用以下方法實現精確…

這是開玩笑嗎?加個工具,WPS與Excel表格變成了應用系統

表格處理數據簡單快捷&#xff0c;是個人用戶的首選。然而&#xff0c;當企業長期使用&#xff0c;成本表、客戶表、銷售表等堆積如山&#xff0c;尋找所需表格如同大海撈針&#xff0c;稍有不慎便可能導致數據丟失、混亂。即便使用WPS和Excel這樣的表格軟件&#xff0c;處理大…

代碼隨想錄算法訓練營第三十六天 | LeeCode 435. 無重疊區間 ,763.劃分字母區間 , 56. 合并區間

435. 無重疊區間 - 力扣&#xff08;LeetCode&#xff09; class Solution { private:static bool cmp(const vector<int> &a,const vector<int> &b){if(a[0]b[0]) return a[1]<b[1];return a[0]<b[0];} public:int eraseOverlapIntervals(vector&l…

C#進階高級語法之LINQ:查詢操作的便利性與效率提升

引言&#xff1a; 在C#編程中&#xff0c;LINQ&#xff08;Language-Integrated Query&#xff09;是一種強大的查詢語言&#xff0c;它被集成在.NET框架中&#xff0c;允許開發者對各種數據源進行查詢和操作。LINQ的出現&#xff0c;極大地提升了C#在數據處理方面的能力&#…

回溯難題(算法村第十八關黃金挑戰)

復原 IP 地址 93. 復原 IP 地址 - 力扣&#xff08;LeetCode&#xff09; 有效 IP 地址 正好由四個整數&#xff08;每個整數位于 0 到 255 之間組成&#xff0c;且不能含有前導 0&#xff09;&#xff0c;整數之間用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 &q…

IDEA中使用git提交代碼時,有.class文件怎么避免

在IDEA中使用git提交代碼時&#xff0c;git把.class文件都給我放進來了&#xff0c;而我并不想要提交.class文件 我要提交的是.java文件 應該怎么設置呢 解決方案&#xff0c;點擊整個項目的生命周期中的clean之前&#xff0c;你會發現git提交欄的.class文件都不見了。

常用LDO型號

常用LDO型號 常用LDO型號-國產&進口 常用的LDO&#xff08;低壓差線性穩壓器&#xff09;型號有以下這些&#xff1a; LM2937及LM2937-N&#xff1a;這兩款是TI&#xff08;德州儀器&#xff09;的產品&#xff0c;其中LM2937-N為低噪聲版本&#xff0c;適用于對噪聲敏感…

vue是如何監聽對象和數組變化的

Vue框架通過其響應式系統來監聽對象和數組的變化。這個系統的核心在于追蹤依賴關系&#xff0c;并在數據變化時通知所有依賴于該數據的觀察者。 1. 對象監聽 Vue使用Object.defineProperty方法來劫持各個屬性的getter和setter。當組件中的數據被讀取時&#xff0c;會觸發gette…

ROS2服務通信的實現

文章目錄 1.服務通信的概念及應用場景1.1概念1.2 應用場景 2.準備工作3.服務通信的實現3.1 服務通信接口消息3.2 服務端實現3.3 客戶端實現3.4 編譯及運行3.4.1 修改CMakeLists3.4.2 服務端運行結果3.4.2 客戶端運行結果 1.服務通信的概念及應用場景 1.1概念 服務通信也是ROS…

抖店0元入駐不交錢會怎么樣?個人店和個體店的利弊分析,開店必看

我是王路飛。 現在的抖店是可以開通個人店的。 也就是不需要營業執照、直接使用個人身份證就可以在抖音開店&#xff0c;而且也不需要繳納店鋪保證金就能開店運營了。 但真實情況是怎么樣的呢&#xff1f;新手0元入駐抖店不交這個保證金會怎么樣呢&#xff1f; 今天給想在抖…

AI大預言模型——ChatGPT在地學、GIS、氣象、農業、生態、環境應用

原文鏈接&#xff1a;AI大預言模型——ChatGPT在地學、GIS、氣象、農業、生態、環境應用 一開啟大模型 1 開啟大模型 1)大模型的發展歷程與最新功能 2)大模型的強大功能與應用場景 3)國內外經典大模型&#xff08;ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diff…

ios App 發送廣播失敗解決

記錄開發 ios App 使用 c 混編時遇到的問題&#xff1a; 開發環境 macOS Sonoma&#xff08;最新版本14.3.1&#xff09; Xcode Version 15.2 ipadOS&#xff08;最新版本17.3.1&#xff09; 問題&#xff1a;在mac 上 和 ipad上測試&#xff0c;當 udp 發送廣播&#xff…

跨域引起的兩個接口的session_id不是同一個

來源場景&#xff1a; RequestMapping(“/captcha”)接口設置了SESSION_KEY&#xff0c;也能獲取到&#xff0c;但是到了PostMapping(“/login”)接口就是空的&#xff0c;由于跨域導致的兩個session_id不是同一個 /*** 系統用戶 前端控制器*/ Controller CrossOrigin(origins…

【數據結構和算法初階(C語言)】雙向循環帶頭鏈表的增刪查改詳解(天才設計的鏈表結構,應用簡單逆天!!!!!)

目錄 ?編輯?編輯 1.雙向鏈表的定義&#xff1a;前赴后繼 2.帶頭鏈表的定義-----哨兵位 3.增刪查改 3.1創建新節點函數----方便后續增加節點調用 3.2創建哨兵位----創建頭結點 3.3增加節點&#xff0c;尾部插入數據 3.4尾刪除 3.5查找函數----遍歷對比&#xff…

AcWing 562.壁畫

咱先看一眼算法標簽&#xff0c;發現是思維題、枚舉、前綴和 Buttt其實我們根據上訴的樣例解釋部分就會發現&#xff0c;其實這就是一個長度為?n/2?&#xff08;向上取整哦&#xff09;的連續子數組的最大和。 這題我也用暴力法試過啦&#xff0c;很明顯會TLE 如果你對dp題…