# canvas

组件类型:UniCanvasElement

画布

# 兼容性

Web Android iOS
4.21 4.25 4.25

App平台4.25之前没有完整的canvas组件,但提供了drawablecontext

  • 每个view,都可以通过draw API,高性能的画各种形状、写字。这组API与web的canvas api接近但不同。
  • 截图或海报需求,无需像webview那样通过canvas中转,app平台view直接提供截图API,takesnapshot
  • 二维码展示需求:见插件市场
  • 图表需求:插件市场搜echart插件市场搜F2
  • 手写签名:见插件市场
  • 使用web-view中的canvas也是一种方案,uvue页面里的web-view组件可以和uvue页面里的uts代码双向通信。

从4.25起,App平台也补充了canvas组件。

canvas组件和drawablecontext的区别是:

  1. canvas组件的语法是W3C标准语法
  2. canvas组件全平台支持,而drawablecontext仅app支持
  3. canvas组件是一个独立组件,而drawablecontext是对现有的view组件进行绘制
  4. drawablecontext在iOS上绘制文字的性能不佳,其原生系统如此
  5. 对于复杂绘制场景,比如游戏,canvas组件的性能优于drawablecontext
  6. canvas是一个独立模块,占用几百K体积,不使用时会被摇树摇掉

# API

老版 uni-app 的 canvas 使用了微信小程序的的旧版规范,和 W3C 规范有差异。微信小程序新版的 canvas 规范已经与 W3C 规范拉齐。

uni-app x 中废弃了老版方案,使用了 W3C 规范和微信小程序的新版规范。

注意:在uni-app x 4.21版以前,Web平台开发者写的老版canvas是可以运行的。但从 4.21+ 支持新版规范起,不再支持老版规范。开发者需调整代码。

注意:新版规范需要开发者根据自己的场景手动处理高清屏问题。

canvas相关的API非常多,参考如下:

# 获取组件上下文对象CanvasContext

  1. 异步方式获取CanvasContext

这种方式跨平台,一般推荐这种写法。需HBuilderX 4.25+支持。

<template>
  <canvas id="canvas"></canvas>
</template>
<script setup>
// HBuilderX 4.25+
// 异步调用方式, 跨平台写法
uni.createCanvasContextAsync({
  id: 'canvas',
  component: this,
  success: (context : CanvasContext) => {
    const canvasContext = context.getContext('2d')!;
    const canvas = canvasContext.canvas;

    // 处理高清屏逻辑
    const dpr = uni.getDeviceInfo().devicePixelRatio ?? 1;
    canvas.width = canvas.offsetWidth * dpr;
    canvas.height = canvas.offsetHeight * dpr;
    canvasContext.scale(dpr, dpr); // 仅需调用一次,当调用 reset 方法后需要再次 scale
  }
})
</script>

文档详见

  1. 同步方式CanvasContext

需HBuilderX 4.21+支持。

<template>
  <canvas id="canvas"></canvas>
</template>
<script setup>
// 同步调用方式,仅支持 app/web
const canvas = uni.getElementById("canvas") as UniCanvasElement
const context = canvas.getContext("2d")!;

// 处理高清屏逻辑
const dpr = uni.getDeviceInfo().devicePixelRatio ?? 1;
canvas.width = canvas.offsetWidth * dpr;
canvas.height = canvas.offsetHeight * dpr;
context.scale(dpr, dpr); // 仅需调用一次,当调用 reset 方法后需要再次 scale

// 省略绘制代码,和 w3c 规范保持一致
</script>

# 示例

hello uni-app x

扫码体验(手机浏览器跳转到App直达页)

Template

Script

<template>
  <view class="page" id="page-canvas">
    <canvas id="canvas" class="canvas-element"></canvas>
    <scroll-view class="scroll-view">
      <!-- #ifdef WEB -->
      <button class="canvas-drawing-button" @click="canvasToBlob">canvasToBlob</button>
      <view>
        <text>testToBlobResult: {{testToBlobResult}}</text>
      </view>
      <!-- #endif -->
      <button class="canvas-drawing-button" id="toDataURL" @click="canvasToDataURL">canvasToDataURL</button>
      <view class="text-group" v-if="dataBase64.length>0">
        <text>canvasToDataURL:</text>
        <text>{{dataBase64.slice(0,22)}}...</text>
      </view>
      <button @click="onCreateImage">createImage</button>
      <button @click="onCreatePath2D">createPath2D</button>
      <button @click="startAnimationFrame">requestAnimationFrame</button>
      <button @click="stopAnimationFrame">cancelAnimationFrame</button>
      <view style="padding: 8px 10px;">CanvasContext API 演示</view>
      <navigator url="./canvas-context">
        <button>CanvasContext API</button>
      </navigator>

      <view class="text-group">
        <text>获取 CanvasContext 结果:</text>
        <text id="testCanvasContext">{{testCanvasContext}}</text>
      </view>
      <view class="text-group">
        <text>测试 ToDataURL 结果:</text>
        <text id="testToDataURLResult">{{testToDataURLResult}}</text>
      </view>

      <view class="text-group">
        <text>测试 createImage 结果:</text>
        <text id="testCreateImage">{{testCreateImage}}</text>
      </view>

      <view class="text-group">
        <text>测试 createPath2D 结果:</text>
        <text id="testCreatePath2D">{{testCreatePath2D}}</text>
      </view>
    </scroll-view>
  </view>
</template>



<style>
  .page {
    flex: 1;
    height: 100%;
    overflow: hidden;
  }

  .scroll-view {
    flex: 1;
  }

  .canvas-element {
    width: 100%;
    height: 250px;
    background-color: #ffffff;
  }

  .btn-to-image {
    margin: 10px;
  }

  .text-group {
    display: flex;
    flex-flow: row nowrap;
    justify-content: space-between;
    align-items: center;
    padding: 8px 10px;
  }
</style>

# 参见