微信 Canvas 代码包 示例

作者猫斯基 2022-08-31 435 人读过

最新文章

转载 尚未验证

作为资深的开发者,相信大家对 Canvas 都不陌生。这项能力在绘制图形方面发挥着极大的作用,高效支持图片编辑、数据可视化等应用场景。但是只局限于一般能力应用,那格局就小了图片

Canvas 的应用场景非常丰富!赶紧往下看看这些隐藏的 Canvas 小技巧,保证你新年用得上!还有手把手教程以及文末彩蛋哟。

绘制并生成图片

适用场景:图片分享海报

相关 API:RenderingContext/Canvas/wx.canvasToTempFilePath

Step 1: 创建实例获取对象

创建 Canvas 实例,获取 CanvasRenderingContext2D 对象(Canvas 绘图上下文)来绘制形状、文本、图像等。

<!-- canvas 2d -->
<canvas type="2d" id="myCanvas"></canvas>
const query = wx.createSelectorQuery()
let canvas = null
query.select('#myCanvas')
  .fields({ node: true, size: true })
  .exec((res) => {
    // 通过 wx.createSelectorQuery 获取到 canvas 实例
    canvas = res[0].node
  // 通过 canvas.getContext('2d') 获取 CanvasRenderingContext2D 对象
    const ctx = canvas.getContext('2d')
  })

Step 2: 设置宽高调整图片

获取 Canvas 绘图上下文后,将 Canvas 的宽高设置为节点宽高 * 设备像素比,绘制出来的图片更清晰

// 获取设备像素比
const dpr = wx.getSystemInfoSync().pixelRatio
// 将 canvas 宽高设置为
canvas.width = res[0].width * dpr
canvas.height = res[0].height * dpr

Step 3: 绘制内容

使用 CanvasRenderingContext2D 绘制,根据业务需要在画布中绘制头像、文字、背景等

// 矩形
ctx.fillStyle = '#FFFFFF'
ctx.fillRect(0, 0, canvas.width , canvas.height )

// 图片
var image = canvas.createImage()
himage.src = 'https://example.com/example.jpg'
headImage.onload = (res) => {
  ctx.drawImage(himage 0, 0, 32, 32;
}

// 文本
ctx.font = "18px SimHei";
ctx.textAlgin = "left"
ctx.fillStyle = "#07c160";
ctx.fillText("这是我的名字", 0, 0);

Step 4: 生成并保存本地

使用 wx.canvasToTempFilePath 将画布生成图片,wx.saveImageToPhotosAlbum 将图片保存到本地

wx.canvasToTempFilePath({
  canvas: canvas, // canvas 实例
    success(res) {
    // canvas 生成图片成功
        wx.saveImageToPhotosAlbum({
          filePath: res.tempFilePath,
          success(res) {
              // 保存成功
          }
        })
    }
})

Video 绘制 Canvas / webgl

适用场景:制作 Video 滤镜、挑选 Video 封面等

相关 API:RenderingContext/Canvas

Step 1: 获取实例

通过 wx.createSelectorQuery 获取 VideoContext 实例

let video = null
wx.createSelectorQuery().select('#video').context(res => {
  // 通过 wx.createSelectorQuery 获取 VideoContext 实例
    video = res.context;   
})

Step 2: 绘制内容

获取 VideoContext 实例后,将 VideoContext 传递给 Canvas 进行绘制。开发者根据业务需求选择绘制类型:

Canvas 2d 写法:canvas.drawImage(video, …)

webgl 写法:gl.texImage2D(…, video)

<!-- canvas 2d -->
<canvas type="2d" id="myCanvas"></canvas>
<!-- canvas webgl -->
<canvas type="webgl" id="webglCanvas"></canvas>
wx.createSelectorQuery().selectAll('#myCanvas,#webglCanvas').node(res => {

  const ctx = res[0].node.getContext('2d')
    const gl = res[1].node.getContext('webgl')

    setInterval(() => {
    // canvas 2d
    // 将 video 纹理对象传入 drawImage 进行绘制
      ctx1.drawImage(video, 0, 0, w * dpr, h * dpr);
    // 添加一个蒙层
      ctx1.fillStyle = 'rgba(0, 0, 0, 0.3)'
      ctx1.fillRect(0, 0, w * dpr, h * dpr);

    // webgl
      const render = createRenderer(res[1].node, w, h)
      render(new Uint8Array(ctx1.getImageData(0, 0, w * dpr, h * dpr).data), w * dpr, h * dpr)

    }, 1000 / 24)
}).exec()

function createRenderer(canvas, width, height) {
  const gl = canvas.getContext("webgl")
  ...
  return (arrayBuffer, width, height) => {
      ...
      // 指定二维纹理图像
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBuffer)
      gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0)
    }
}

视频解码并绘制到 webgl

适用场景:添加特效、贴图等视频编辑场景

相关 API:wx.createVideoDecoder/VideoDecoder/RenderingContext/Canvas.requestAnimationFrame/wx.createMediaAudioPlayer/MediaAudioPlayer

Step 1: 创建视频解码器进行解码

1. 调用 createVideoDecoder 对视频进行解码
2. 使用 videodecoder.start 启动解码,视频源文件不限制本地或远程路径
3. 通过 videodecoder.on(‘start’, res => {}) 监听解码,通过 videodecoder.getFrameData() 获取到解码数据

// 获取视频解码器
getVideoDecoder(source, abortAudio) {
  return new Promise((resolve, reject) => {
  // 创建视频解码器
    videodecoder = wx.createVideoDecoder()
    // 开始解码
    videodecoder.start({
      abortAudio: abortAudio,
      source: source, // 视频源文件,支持本地路径&远程路径
      mode: 0 // 按pts解码,保证音画同步
    })
    // 监听解码 开始
    videodecoder.on('start', res => {
      console.log('videodecoder start', res)
      // 状态初始化
      isStop = false
      resolve(videodecoder)
    })
    // 监听解码 结束
    videodecoder.on('ended', res => {
      // 状态设置为结束,停止画面录制器
      isStop = true
    })
  })
},

Step 2: 解码数据绘制到 webgl

1. 通过 gl.texImage2D(…, image) 将解码数据绘制到 webgl

2. 使用 webgl.requestAnimationFrame 继续绘制,效果更加流畅

<!-- canvas webgl -->
<canvas type="webgl" id="webglCanvas"></canvas>
// 将解码数据绘制到 webgl 中
const query = wx.createSelectorQuery()
query.select('#webglCanvas').node().exec((res) => {
  const webgl  = res[0].node
  const requestAnimationFrame = webgl.requestAnimationFrame;
  // 初始化webgl
  let render = null
  if (!render) {
    render = createRenderer(webgl, 600, 400)
  }

  /**
   * 绘制视频帧到 canvas
   */
  let i = 1
  let loop = () => {
    // 解码结束,停止循环
    if (isStop) {
      return
    }
    // 获取解码数据,绘制到 webgl 中
    const imageData = videodecoder.getFrameData()
    if (imageData) {
      // render 的高宽需要设置为图片的宽高才可以绘制出来
      render(new Uint8Array(imageData.data), imageData.width, imageData.height)
    }
    // 继续绘制
    console.log('绘制帧数:', i++)
    requestAnimationFrame(loop)
  }  
  // 启动录制循环 
  requestAnimationFrame(loop)
})

Step 3: 添加音频播放器同步播放音频

完成 Step2 后,webgl 只有视频播放,缺少音频。因此使用 wx.createMediaAudioPlayer(),支持 addAudioSource 传入 videodecoder,保证视频帧渲染音画同步

/**
 * 创建媒体音频播放器
 */ 
let mediaAudioPlayer = null
let addAudio = () => {
  if (mediaAudioPlayer) return
  mediaAudioPlayer = wx.createMediaAudioPlayer()
  mediaAudioPlayer.start().then(() => {
    // 添加播放器音频来源
    mediaAudioPlayer.addAudioSource(videodecoder).then(res => {
      console.log('add mediaAudioPlayer: ',)
    })
  })
}

// render 绘制视频同时添加音频
render(new Uint8Array(imageData.data), imageData.width, imageData.height)
addAudio()

录制并导出 webgl 视频

适用场景:将动画、编辑过的视频导出视频文件保存

相关 API:wx.createMediaRecorder/MediaRecorder/wx.createMediaContainer/MediaContainer/MediaTrack

Step 1: 创建 webgl 画面录制器进行录制

通过 createMediaRecorder 创建页面录制器,并且绑定 webgl(建议离屏状态,效果更好)进行录制

/**
 * 获取画面录制器
 */
getRecorder() {
  let canvas = this.getMainCanvasNode()

  let recorder = wx.createMediaRecorder(canvas, {
    fps: choosedVideoInfo.fps, // 实际视频的 fps
    videoBitsPerSecond: choosedVideoInfo.bitrate, // 实际视频的 bitrate
    gop: 12
  })
  // 监听录制事件
  recorder.on("timeupdate", (res) => {
    console.log('recorder 录制中,当前时间:', res.currentTime)
  })
  recorder.on("stop", (res) => {
    console.log('recorder停止')
    this.saveMedia(res.tempFilePath)
  })
  // 开始录制
  recorder.start()
  this.recorder = recorder
  return recorder
},

// 初始化 画面录制器 并进行录制
await this.initRenderer()
this.getDecoder().then((decoder) => {
  let recorder = this.getRecorder()
  var self = this
  function loop() {
    if (self.stopped) {
      return
    }
    let frameData = decoder.getFrameData()
    if (!frameData) {
      console.log('没取到帧')
      setTimeout(() => {
        loop()
      }, 1000/60)
    } else {
      self.renderFrame(frameData)
      recorder.requestFrame(() => {
        console.log('录制帧数:', i++)
        loop()
      })
    }
  }
  loop()
})

Step 2: 添加音频合成音视频

1. 通过 createMediaContainer 创建音视频处理容器来合成音视频
2. 通过 MediaContainer.extractDataSource 将视频源分离出视频轨道和音频轨道,将需要的轨道通过 MediaContainer.addTrack 添加到容器中
3. 通过 MediaContainer.export 导出即可获得合成后的视频文件

/**
 * 将视频和音频合到一起并保存到本地
 * @param {*} videoTempFilePath 
 */
saveMedia(videoTempFilePath) {
  const self = this
  let choosedFile = this.choosedFile
  const MediaContainer = wx.createMediaContainer()
  // webgl的取视频
  MediaContainer.extractDataSource({
    source: videoTempFilePath,
    success(res) {
      MediaContainer.addTrack(res.tracks[0])

      // 源视频取音频
      MediaContainer.extractDataSource({
        source: choosedFile,
        success(res) {
          // 拿到音频轨道并加入到容器
          res.tracks[0].kind == 'audio' && MediaContainer.addTrack(res.tracks[0])
          res.tracks[1].kind == 'audio' && MediaContainer.addTrack(res.tracks[1])

          // 合成视频并导出视频文件
          MediaContainer.export({
            success(res) {
              // 保存视频到本地
              wx.saveVideoToPhotosAlbum({
                filePath: res.tempFilePath,
                success() {
                  wx.showToast({
                    title: '导出成功',
                    icon: 'success',
                    duration: 2000
                  })
                  self.destroy()
                }
              })
            }
          })
        }
      })
    }
  })

},

本文由 猫斯基 转载发布。

转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。本网转载其他媒体之稿件,意在为公共提供免费服务。如稿件版权单位或个人不想再本网发布,可与本网联系,核实后将立即将其删除。 本文地址:https://www.maosiji.com/2925.html

转载地址:微信

关注我们

站长

WordPress迷