• 2023 uniapp( vue3 + TS )使用canvas生成海报并保存,taro/微信小程序也适用


    有段时间没写vue了,有点生疏了......

    1、代码有注释,完整代码如下

    1.1、工具函数 - canvas.ts

    1. //utils/canvas.ts
    2. const fs = uni.getFileSystemManager()
    3. // 将Base64写入本地文件
    4. const base64WriteFile = (filePath : string, data : string) => {
    5. return new Promise((resolve, reject) => {
    6. fs.writeFile({
    7. filePath,
    8. data,
    9. encoding: 'base64',
    10. success: (res) => {
    11. resolve(res);
    12. },
    13. fail: (err) => {
    14. reject(err);
    15. },
    16. });
    17. });
    18. };
    19. // 参数的类型校验
    20. type GetImgBase64Type = {
    21. src : string;//图片地址(本地/在线地址)
    22. canvasWidth : number;//画布宽度
    23. filePath : string//临时路径
    24. }
    25. // 加载图片地址,生成base64并写入临时路径中
    26. export const getImgBase64 = async (params : GetImgBase64Type) => {
    27. const { src, canvasWidth, filePath } = params
    28. try {
    29. // 获取图片信息:地址、宽高
    30. const imgInfo = await uni.getImageInfo({
    31. src,
    32. });
    33. // 计算图片在画布中的宽度
    34. const imageWidth = canvasWidth * 0.8;//随便定的,多少px都行
    35. // // 根据比例计算图片在画布中的高度
    36. const scaleFactor = Number((imageWidth / imgInfo.width).toFixed(2));
    37. // 根据比例计算图片高度
    38. const imageHeight = imgInfo.height * scaleFactor;
    39. // 生成base64
    40. const base64 : any = fs.readFileSync(imgInfo.path, 'base64')
    41. // 写入本地
    42. await base64WriteFile(filePath, base64)
    43. const currentImgInfo = await uni.getImageInfo({
    44. src: filePath,
    45. });
    46. return {
    47. imageWidth,
    48. imageHeight,
    49. imgUrl: currentImgInfo.path
    50. }
    51. } catch (err) {
    52. console.log('err', err);
    53. }
    54. };
    55. type DrawRoundedRectParamsType = {
    56. leftTop ?: boolean;
    57. leftBottom ?: boolean;
    58. rightTop ?: boolean;
    59. rightBottom ?: boolean;
    60. fillColor ?: string;
    61. r ?: number;
    62. };
    63. // canvas 绘制自定义圆角矩形
    64. export const drawRoundedRect = (
    65. ctx : any,
    66. x : number,
    67. y : number,
    68. w : number,
    69. h : number,
    70. params ?: DrawRoundedRectParamsType,
    71. ) => {
    72. const {
    73. leftTop = false,
    74. leftBottom = false,
    75. rightTop = false,
    76. rightBottom = false,
    77. fillColor = 'transparent',
    78. r = 0,
    79. } = params || {};
    80. ctx.save(); // 保存当前绘图状态 防止虚线影响其他图形
    81. ctx.beginPath();
    82. ctx.setFillStyle(fillColor);
    83. ctx.setStrokeStyle('transparent');
    84. ctx.moveTo(x + r, y);
    85. // 绘制上边线和左上角圆弧
    86. if (leftTop) {
    87. ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
    88. ctx.lineTo(x, y);
    89. } else {
    90. ctx.moveTo(x, y + r);
    91. ctx.lineTo(x, y);
    92. ctx.lineTo(x + r, y);
    93. }
    94. ctx.lineTo(x + w - r, y);
    95. // 绘制上边线和右上角圆弧
    96. if (rightTop) {
    97. ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
    98. } else {
    99. ctx.lineTo(x + w - r, y);
    100. ctx.lineTo(x + w, y);
    101. ctx.lineTo(x + w, y + r);
    102. }
    103. ctx.lineTo(x + w, y + h - r);
    104. // 绘制下边线和右下角圆弧
    105. if (rightBottom) {
    106. ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
    107. } else {
    108. ctx.lineTo(x + w, y + h - r);
    109. ctx.lineTo(x + w, y + h);
    110. ctx.lineTo(x + w - r, y + h);
    111. }
    112. ctx.lineTo(x + r, y + h);
    113. // 绘制下边线和左下角圆弧
    114. if (leftBottom) {
    115. ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
    116. } else {
    117. ctx.lineTo(x + r, y + h);
    118. ctx.lineTo(x, y + h);
    119. ctx.lineTo(x, y + h - r);
    120. }
    121. ctx.lineTo(x, y + r);
    122. // 绘制左边线和左上角圆弧
    123. if (leftTop) {
    124. ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
    125. ctx.moveTo(x + r, y);
    126. } else {
    127. ctx.moveTo(x, y + r);
    128. ctx.lineTo(x, y);
    129. ctx.lineTo(x + r, y);
    130. }
    131. ctx.fill();
    132. ctx.closePath();
    133. ctx.stroke();
    134. ctx.restore(); // 恢复之前的绘图状态
    135. };
    136. type DrawTextConfigType = {
    137. ctx : any;
    138. fillStyle : string;//填充颜色
    139. fontSize : number//文字大小
    140. text : string;//在画布上输出的文本
    141. x : number;//绘制文本的左上角x坐标位置
    142. y : number//绘制文本的左上角y坐标位置
    143. center ?: boolean
    144. }
    145. // 绘制文本
    146. export const drawText = (config : DrawTextConfigType) => {
    147. const { fillStyle, fontSize, x, y, text, ctx, center = false } = config
    148. ctx.setFillStyle(fillStyle);
    149. ctx.setFontSize(fontSize);
    150. if (center) {
    151. ctx.textAlign = 'center';//文字水平居中
    152. }
    153. ctx.fillText(text, x, y);
    154. }
    155. // 获取当前设备信息
    156. export const getSystemInfo = () => {
    157. return new Promise((resolve) => {
    158. uni.getSystemInfo({
    159. success(res) {
    160. resolve(res)
    161. },
    162. })
    163. })
    164. }

    1.2、工具函数 - index.ts

    1. //utils/index.ts
    2. // 获取用户授权
    3. type GetAuthorizeType = {
    4. title ?: string;//授权弹框描述
    5. callback ?: () => void//成功的回调
    6. }
    7. export const getAuthorize = (scope : string, params : GetAuthorizeType) => {
    8. const { title = '请开启授权', callback } = params
    9. return new Promise(() => {
    10. uni.authorize({
    11. scope,
    12. success: () => {
    13. callback?.()
    14. },
    15. fail: () => {
    16. // 如果用户点了拒绝,需要弹框提示再次授权
    17. uni.showModal({
    18. title,
    19. success() {
    20. uni.openSetting();
    21. },
    22. });
    23. }
    24. })
    25. })
    26. }

    1.3、图片列表函数

    1. // ./utils/index.ts
    2. export type ImageListType = {
    3. id : number;
    4. name : string
    5. desc : string
    6. imageSrc : string
    7. bgColor : string
    8. pageColor : string
    9. }
    10. export const imageList : ImageListType[] = [
    11. {
    12. id: 0,
    13. name: '那维莱特',
    14. desc: '潮水啊,我已归来!',
    15. imageSrc: '../../static/那维莱特.jpg',
    16. bgColor: '#b2d4ff',
    17. pageColor: '#d9e9ff',
    18. },
    19. {
    20. id: 1,
    21. name: '东方镜',
    22. desc: '太阳之下,诸世皆影!',
    23. imageSrc: '../../static/镜.jpg',
    24. bgColor: '#ffdecd',
    25. pageColor: '#fff3ed',
    26. },
    27. {
    28. id: 2,
    29. name: '魈',
    30. desc: '你去吧,我会在这里等你。',
    31. imageSrc: '../../static/魈.png',
    32. bgColor: '#f1ddff',
    33. pageColor: '#fbf4ff',
    34. },
    35. {
    36. id: 3,
    37. name: '琴团长',
    38. desc: '我以此剑起誓,必将胜利献给你!',
    39. imageSrc: '../../static/琴.jpg',
    40. bgColor: '#e6e4ff',
    41. pageColor: '#f7f6ff',
    42. },
    43. ]

    2、效果如下

                                    

                    

    3、添加相册授权

    根据各自框架添加授权即可,比如uniapp在manifest.json下

    1. "mp-weixin" : {
    2. "appid" : "你的微信appid",
    3. "setting" : {
    4. "urlCheck" : false
    5. },
    6. "usingComponents" : true,
    7. /* 授权 */
    8. "permission": {
    9. "scope.writePhotosAlbum": {
    10. "desc": "请授权保存到相册"
    11. }
    12. }
    13. },

    4、项目地址

    我的项目地址,点击跳转

    5、问题总汇

    5.1、为什么本地图片/在线图片真机不显示等?

    将所有用到的图片转 base64 展示,参考上面工具函数中的 getImgBase64()

    5.2、多文本如何换行?

    参考下面地址  使用canvas画布时多行文本应该怎么换行? | 微信开放社区  

    5.3、多次绘制出现白屏等?

    比如以弹框的形式多次点击生成等情况,首先要确保每个canvas-idID的实例不能重复。可以参考我上面标题1中的代码。

    5.4、当ctx.draw()后需要立马回去临时路径做 image预览时,画布生成的内容不全?

    5.4.1、前提情景

    由于 canvas 的层级比较高,做预览的时候会遮住其他的view等标签。而且样式或拖拽等也不好处理,花费时间肯定更多一点,这个时候需要用 代替 canvas 做展示。

    5.4.2、解决

    改写ctx.draw()为如下:

    1. ctx.draw(
    2. false,
    3. setTimeout(async () => {
    4. //在这里生成临时路径
    5. const { tempFilePath } = await uni.canvasToTempFilePath({
    6. canvasId: canvasId.value,
    7. });
    8. console.log('tempFilePath', tempFilePath);
    9. await uni.saveImageToPhotosAlbum({
    10. filePath: tempFilePath
    11. })
    12. }, 100),
    13. );

    由于绘制可能需要更长的时间,通过延时器即可解决。

  • 相关阅读:
    [ISITDTU 2019]EasyPHP
    据采集的三种方式-如何获取数据
    竞赛 题目:基于卷积神经网络的手写字符识别 - 深度学习
    easyExcel应用
    细胞不可渗透的荧光探针 锌离子荧光探针Zinquin 151606-29-0
    四参数旋转角异常,平面坐标方位角不准确的问题
    如何实现模拟量信号远距离无线传输?
    如何在.NET程序崩溃时自动创建Dump?
    易点易动固定资产管理系统:高效完成固定资产盘点任务
    windows10/11子系统安装ubuntu22.04
  • 原文地址:https://blog.csdn.net/muge1161105403/article/details/133947472