小程序 canvas 生成海报 一次搞掂

简介: 小程序 canvas 生成海报 一次搞掂

小程序 canvas 生成海报 - 解决屏幕图片失真,解决无法使用外网图片

源代码在最下方

最终结果

canvas(画布) 元素用于在网页上绘制图形。画布是一个矩形区域,您可以控制其每一像素。canvas 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。

注意

需要注意的是,目前的canvas可以简单分为两种。一种是传统网页中的canvas,一种是小程序中的canvas。两者的功能是完全一样的。只是标签的样式,和 api 略有区别而已。目前我们主要讲解小程序中的canvas。

canvas 的应用场景

  1. 在线游戏
  2. 在线图表
  3. 页面特效
  4. 广告
  5. 图片合成小程序中常见
  1. 点我加速


  1. 头像红旗


  1. 海报日历

  1. 其他



简单体验

我们来画一条直线

在canvas中,把画直线的步骤分解为以下几步:

  1. 编写标签
  2. 获取画布实例
  3. 定起点
  4. 连接终点
  5. 连线 (也叫描边)
  6. 上色

编写标签

默认的宽高 为 300px * 150 px

不同于普通的标签,必须要提供一个属性 canvas-id,用于在 js中获取该对象(不是dom对象!!!)

<canvas canvas-id="firstCanvas"></canvas>

获取画布实例

通过 canvas-id 来获取

该实例 不是dom元素,可以理解为另一种对象如 Math Date String等即可

index.js


Page({
  onLoad() {
    // 1 获取画布上下文对象
    const context = wx.createCanvasContext("firstCanvas");
    console.log(context);
  }
})



点起点

在canvas中,存在一个坐标系 如下图:

我们在canvas中所讲的坐标都是相对于canvas内部坐标而言

定个起点


// 定起点
    context.moveTo(10, 10);
 

定终点


// 定终点
  context.lineTo(300,150);

连线


// 连线
    context.stroke();

上色


// 上色
    context.draw();

完整代码

index.wxml


<!-- 1 写标签 -->
<canvas canvas-id="firstCanvas"></canvas>

index.js


Page({
  onLoad() {
    // 2 获取画布上下文对象
    const context = wx.createCanvasContext("firstCanvas");
    // 3 定起点
    context.moveTo(10, 10);
    // 4 定终点
    context.lineTo(300,150);
    // 5 连线
    context.stroke();
    // 6 上色
    context.draw();
  }
})

效果

内置的其他规则图形

canvas中还封装了画规则图形的方法,如:

  1. 画空心的矩形
  2. 画圆弧
  3. 画实心的矩形
  4. 画文字(把字符串画上去)

画矩形

CanvasContext.strokeRect(number x, number y, number width, number height)

CanvasContext.strokeRect(画在画布的X,画在画布的Y,画多宽,画多高)


// 1 获取画布上下文对象
    const context = wx.createCanvasContext("firstCanvas");
    // 2 调用canvas内置的画“矩形”的方法
    context.strokeRect(10, 10, 100, 100);
    // 3 上色 
    context.draw();

效果

画圆弧

CanvasContext.arc(number x, number y, number r, number sAngle, number eAngle, boolean counterclockwise)

CanvasContext.arc(圆心的横坐标X,圆心的纵坐标Y, 半径的长度, 开始的弧度, 结束的弧度, ?是否反向来画)

代码


drawArc() {
    // 1 获取画布上下文对象
    const context = wx.createCanvasContext("firstCanvas");
    // context.arc(圆心的横坐标X,圆心的纵坐标Y, 半径的长度, 开始的弧度, 结束的弧度);
    // 2 调用内置的画 “圆弧” 的方法
    context.arc(100, 100, 100, this.angleToArc(0), this.angleToArc(90));
    // 3 开始描边
    context.stroke();
    // 4 上色
    context.draw();
  },
  /**
   * 将角度转为弧度
   * @param {number} angle 角度
   */
  angleToArc(angle) {
    return angle * Math.PI / 180;
  }

效果

画实心的矩形

CanvasContext.fillRect(number x, number y, number width, number height)


// 1 获取画布上下文对象
    const context = wx.createCanvasContext("firstCanvas");
    // 2 调用canvas内置的 画填充 “矩形”的方法
    context.fillRect(10, 10, 100, 100);
    // 3 上色 
    context.draw();
 

效果

画文字

CanvasContext.strokeText(string text, number x, number y, number maxWidth)

CanvasContext.strokeText(要绘制的文本, 文本起始点的 x 轴坐标, number y, 需要绘制的最大宽度,可选)

代码


// 1 获取画布上下文对象
    const context = wx.createCanvasContext("firstCanvas");
    // 2 画 “文字”
    context.strokeText("hello world", 100, 100);
    // 3 上色 
    context.draw();

效果


设置样式

经过以上的演示我们也发现,线条的颜色一直是黑色,这肯定是无法满足我们骚跳的心的。现在来学习一下关于设置canvas线条样式相关API。

  1. 设置线条颜色
  2. 设置线条粗细
  3. 设置填充颜色
  4. 设置文本大小

设置线条颜色

**特别要注意 **,setStrokeStyle是个函数,1.9.90版本后停止维护,使用以下的方式来修改。

  1. CanvasContext.setStrokeStyle("red") 已过时,不推荐
  2. CanvasContext.strokeStyle="red"; 正解

代码


const context = wx.createCanvasContext("firstCanvas");
    context.moveTo(10, 10);
    context.lineTo(300, 150);
    // 5 修改颜色 需要在stroke之前修改
    context.strokeStyle = "red";
    context.stroke();
    context.draw();

效果


设置线条粗细

**特别要注意 **,setLineWidth 是个函数,1.9.90版本后停止维护,使用以下的方式来修改。

  1. CanvasContext.setLineWidth(20) 已过时,不推荐
  2. CanvasContext.lineWidth=20; 正解

代码


const context = wx.createCanvasContext("firstCanvas");
    context.moveTo(10, 10);
    context.lineTo(300, 150);
    // 设置线条宽度
    context.lineWidth = 20;
    context.stroke();
    context.draw();

效果


设置填充颜色

**特别要注意 **,setFillStyle 是个函数,1.9.90版本后停止维护,使用以下的方式来修改。

  1. CanvasContext.setFillStyle("red") 已过时,不推荐
  2. CanvasContext.fillStyle="red"; 正解

代码


const context = wx.createCanvasContext("firstCanvas");
    // 设置填充颜色
    context.fillStyle = "red";
    context.fillRect(10, 10, 100, 100);
    context.draw();
 

效果


设置文本大小

**特别要注意 **,setFontSize 是个函数,1.9.90版本后停止维护,使用以下的方式来修改。

  1. CanvasContext.setFontSize("20") 已过时,不推荐
  2. CanvasContext.font="sans-serif"; 正解
  3. font 当前字体样式的属性。符合 CSS font 语法 的 DOMString 字符串,至少需要提供字体大小和字体族名。默认值为 10px sans-serif。

代码

const context = wx.createCanvasContext("firstCanvas");
    // 必须要同时提供 字号 和 字体
    context.font="10px  sans-serif";
    context.strokeText("10px", 10, 10);
    // 必须要同时提供 字号 和 字体
    context.font="50px  sans-serif";
    context.strokeText("50px", 50, 100);
    // 必须要同时提供 字号 和 字体
    context.font="80px  sans-serif";
    context.strokeText("80px", 80, 180);
    context.draw();



效果

进阶

在本环节主要讲解稍微复杂一点的功能。要实现以下功能

但是需要先做一点技术铺垫

主要用到的api有:

  1. 获取系统信息
  2. 选择相册图片
  3. 获取网络图片信息
  4. canvas 描绘 图片到画布上
  5. 将画布保存成一张图片
  6. 将图片下载到本地


基本API

以下api是实现以上案例所必须的

获取系统信息

获取屏幕大小、设备像素比等

代码


wx.getSystemInfo({
  success (res) {
    console.log(res.model)
    console.log(res.pixelRatio)
    console.log(res.windowWidth)
    console.log(res.windowHeight)
    console.log(res.language)
    console.log(res.version)
    console.log(res.platform)
  }
})

选择相册图片

从本地相册选择图片或使用相机拍照

代码


wx.chooseImage({
  count: 1,// 最多可以选择的图片张数
  sizeType: ['original', 'compressed'],// 所选的图片的尺寸
  sourceType: ['album', 'camera'],//  选择图片的来源
  success (res) {
    // tempFilePath可以作为img标签的src属性显示图片
    const tempFilePaths = res.tempFilePaths
  }
})

代码


wx.getSystemInfo({
  success (res) {
    console.log(res.model)
    console.log(res.pixelRatio)
    console.log(res.windowWidth)
    console.log(res.windowHeight)
    console.log(res.language)
    console.log(res.version)
    console.log(res.platform)
  }
})

获取网络图片信息

获取图片信息。网络图片需先配置download域名才能生效。

canvas提供了将图片画到画布上的功能,但是要求所提供的图片必须是外网下的图片

因此可以借助该方法将网络图片变成本地图片,同时返回该图片的信息

代码


wx.getImageInfo({
  src: 'cloud://c-73e071.632d-c-73e071/92637.jpg',
  success (res) {
    console.log(res.width)
    console.log(res.height)
  }
})

绘制图像到画布

不能使用本地图片,要使用外网图片的 必须要先 使用 wx.getImageInfo 下载到本地

有三个版本的写法:

  • drawImage(imageResource, dx, dy)
  • drawImage(imageResource, dx, dy, dWidth, dHeight)
  • drawImage(imageResource, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
  • 说明drawImage(图片路径, 原图的x, 原图的y, 原图的宽度, 原图的高度, 画布的x, 画布的y, 画多宽, 画多高)

代码


context.drawImage('xxxx.jpg', 0, 0,100, 100);

将画布保存成一张图片

draw() 回调里调用该方法才能保证图片导出成功

代码


wx.canvasToTempFilePath({
  x: 100,
  y: 200,
  width: 50,
  height: 50,
  destWidth: 100,
  destHeight: 100,
  canvasId: 'myCanvas',
  success(res) {
    console.log(res.tempFilePath)
  }
})

将图片下载到本地

保存图片到系统相册

代码


wx.saveImageToPhotosAlbum({
  success(res) { }
})

案例实现

其实要实现一样案例,最麻烦的不是这些API的调用,而是如何根据不同的图片,合成比例合适不模糊的图片

为什么说比例合适

因为在canvas中,只支持 px 单位,那么在使用javascript来描绘图片时,就不存在 rpxvw%这些相对单位了。只能依靠手动来计算。如,在 canvas中,画出一个大小为 屏幕宽的一半 屏幕高的一半的矩形?

为什么说不模糊

问题的原因还是因为 手机的屏幕 都是高清屏,具体的原因可以参照 链接

如我们想要生成图片大小为 100px * 100px,那么就需要将 canvas的大小设置为 width = 图片的宽度 * 设备像素比

height = 图片的高度 * 设备像素比

文件目录

  1. index 首页
  2. result 合成图片的页面

首页 index

index


pages/index/index.wxml


<!-- 用来显示 被选择的图片的 -->
<image mode="widthFix" src="{{src}}"></image>
<!-- 选择相册图片 -->
<button bindtap="handleTap">选择图片</button>
<!-- 跳转到 结果页面 -->
<button bindtap="handleCreateFlag">生成小红旗</button>

pages/index/index.js

主要实现3个功能

  1. 点击 “选择图片” 将选择的图片打印到页面上
  2. 将被 选择的图片 显示的页面上
  3. 点击 “生成红旗”,跳转到结果页面(在结果页面完成生成)
Page({
  data: {
    src: ""
  },
  // 选择图片
  handleTap() {
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success: (result) => {
        this.setData({
          src: result.tempFilePaths[0]
        })
        // 保存图片路径
        wx.setStorageSync('src', this.data.src);
      }
    });
  },
  // 生成红旗
  handleCreateFlag() {
    // 跳转到结果页面
    wx.navigateTo({
      url: '/pages/result/index'
    });
  }
})

结果页面 result

result


result/index.wxml

3个标签

  1. canvas 标签,通过定位将其隐藏
  2. image 标签,用来显示合成的图片
  3. button 标签,用来点击 下载图片


<!-- canvas 标签-->
<canvas class="cas" style="width:{{canvasWidth}};height:{{canvasHeight}};" canvas-id="firstCanvas"></canvas>
<!-- 用来显示 合成成功的图片 -->
<image class="res_image" mode="widthFix" src="{{resSrc}}"></image>
<!-- 点击下载图片 -->
<button bindtap="handleSave">下载图片</button>

result/index.wxss

两个样式

  1. 把canvas藏起来(因为是 原生组件,所以它的层级比一般的标签都要高(定位+zindex也无法解决))
  2. 设置图片标签的样式


page {
  overflow-x: hidden;
  overflow-y: auto;
  width: 100vw;
  height: 100vh;
}
.cas {
  position: absolute;
  top: 1000vw;
  left: 1000vh;
  z-index: -1;
  opacity: 0;
}
.res_image {
  width: 100%;
  display: block;
}

result/index.js

易错点:

  1. 外网的图片,需要先将图片服务器添加到白名单中(否则真机调试会失败)
  2. 没有动态设置 canvas的宽和高(参照第29、31行)


import regeneratorRuntime from '../../lib/runtime/runtime';
import { getImageInfo, canvasToTempFilePath, saveImageToPhotosAlbum } from "../../wxAsync/index.js";
Page({
  data: {
    // 默认的canvas的宽度
    canvasWidth: 1,
    // 默认的canvas高度
    canvasHeight: 1,
    // 最终生成的图片路径
    resSrc: ""
  },
  // 全局变量
  saveImgSrc: "",
  async onLoad() {
    // 红旗图片
    const flagSrc = "https://632d-c-73e071-1252056196.tcb.qcloud.la/3434.jpg?sign=a4f1c2106d1e61551829c2f99820c0ba&t=1569678566";
    // const baseSrc = "https://632d-c-73e071-1252056196.tcb.qcloud.la/92637.jpg?sign=8952d1eaa69a35510418fe25dc25d6c5&t=1569678606";
    // 上个页面选择的图片路径 柯南图片
    const baseSrc = wx.getStorageSync("src");
    // 设备像素比
    const { pixelRatio } = wx.getSystemInfoSync();
    // 获取 画布实例
    const context = wx.createCanvasContext('firstCanvas');
    console.log(context);
    // 下载到本地的 柯南图片
    const baseImg = await getImageInfo(baseSrc);
    // 下载到本地的 红旗图片
    const flagImg = await getImageInfo(flagSrc);
    // 将canvas的宽度设置中 图片的宽度
    const canvasWidth = baseImg.width + "px";
    // 将canvas的宽度设置中 图片的高度
    const canvasHeight = baseImg.height + "px";
    //  setData 函数用于将数据从逻辑层发送到视图层(异步),同时改变对应的 this.data 的值(同步)。
    // 因此需要将 描绘 图片的步骤写在回调中,否则 真机调试有bug!
    this.setData({ canvasWidth, canvasHeight }, () => {
      // 如果个别机型出现图片失败错误,可以加上定时器。
      setTimeout(() => {
        // 先将柯南 描绘到画布上
        context.drawImage(baseImg.path, 0, 0, baseImg.width, baseImg.height);
        // 把红旗 描绘到画布上
        context.drawImage(flagImg.path, baseImg.width - (pixelRatio * 50), baseImg.height - (pixelRatio * 50), (pixelRatio * 50), (pixelRatio * 50));
        context.draw(true, async () => {
          // 将 画布生成 成图片
          const res1 = await canvasToTempFilePath({
            canvasId: "firstCanvas"
          });
          // 让图片显示 合成后的效果
          this.setData({ resSrc: res1.tempFilePath })
          // 保存起来,当点击保存图片时调用
          this.saveImgSrc = res1.tempFilePath;
        });
      }, 100);
    });
  },
  // 点击保存图片
  handleSave() {
    saveImageToPhotosAlbum(this.saveImgSrc);
  }
})



github地址


代码解读

复制代码

https://github.com/itcastWsy/AppletPoster.git
目录
相关文章
|
2天前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1517 4
|
29天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
5天前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
501 19
|
2天前
|
存储 SQL 关系型数据库
彻底搞懂InnoDB的MVCC多版本并发控制
本文详细介绍了InnoDB存储引擎中的两种并发控制方法:MVCC(多版本并发控制)和LBCC(基于锁的并发控制)。MVCC通过记录版本信息和使用快照读取机制,实现了高并发下的读写操作,而LBCC则通过加锁机制控制并发访问。文章深入探讨了MVCC的工作原理,包括插入、删除、修改流程及查询过程中的快照读取机制。通过多个案例演示了不同隔离级别下MVCC的具体表现,并解释了事务ID的分配和管理方式。最后,对比了四种隔离级别的性能特点,帮助读者理解如何根据具体需求选择合适的隔离级别以优化数据库性能。
179 1
|
8天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
21天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
9天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
451 5
|
7天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
314 2
|
23天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
25天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2608 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析