Top
Fork me on Gitee My Github

Canvas绘制图片合成样式

效果图
最终效果图

web

* {
   margin: 0;
    padding: 0;
  }
  .container {
    position: relative;
    width: 328px;
    height: 328px;
    margin: 100px auto;
  }
  .container img {
    position: absolute;
    width: 328px;
    height: 328px;
  }
  #canvas {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 1;
  }
  .img-shape-bg {
    position: absolute;
    top: 0;
    left: 50%;
    width: 284px;
    height: 284px;
    transform: translateX(-50%) scale(1.4);
    opacity: .05;
  }
<div class="container">
    <img src="icosphere_red@2x.png" alt="">
    <canvas id="canvas" width="140" height="140"></canvas>
    <!--  加一层背景模糊,优化背景色 -->
    <view class="img-shape-bg" style="background-image: radial-gradient( circle, red 50%, transparent, transparent)"></view>
 </div>
function mergeImages() {
   // 创建一个画布
   const canvas = document.getElementById("canvas");
   const context = canvas.getContext("2d");
   const overlay = new Image();
   // 蒙层及样版图片
   overlay.src = "mask@2x.png";
   overlay.onload = function () {
     context.drawImage(overlay, 0, 0, 140, 140);
     const template = new Image();
     template.src = "1.png";
     template.onload = function (res) {
       let width = height = 140
       let realWidth = template.width
       let realHeight = template.height
       let imgRatio = realWidth / realHeight
       let sw = 0, sh = 0, sx = 0, sy = 0;
       let canvasRatio = width / height
       // 图片不变形
       if(imgRatio <= canvasRatio) {
         sw = realWidth
         sh = sw / canvasRatio
         sx = 0
         sy = (realHeight - sh) / 2
       } else { 
         sh = realHeight
         sw = sh * canvasRatio
         sx = (realWidth - sw) / 2 
         sy = 0
       } 
       // 仅在新形状和目标画布重叠的地方绘制新形状。其他的都是透明的
       context.globalCompositeOperation = "source-in";
       context.drawImage(template, sx, sy, sw, sh, 0, 0, 140, 140);
     };
   };
 }
 mergeImages()

miniprogram

.note-top-shape {
    box-sizing: border-box;
    position: relative;
    width: 284rpx;
    height: 284rpx;
    margin: auto;
    .img-shape-main {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -52%);
      width: 120rpx;
      height: 120rpx;
      z-index: 2;
    }
    .img-shape {
      position: relative;
      z-index: 1;
    }
    .img-shape-bg {
      position: absolute;
      top: 0;
      left: 50%;
      width: 284rpx;
      height: 284rpx;
      transform: translateX(-50%) scale(1.5);
      opacity: .05;
    }
  }
// 水晶
 <view class="note-top-shape">
    <image class="img-shape" src="{{ baseImageUrl }}{{ kindShape }}" mode="aspectFit" lazy-load="false" style="width: 284rpx;height: 284rpx;" binderror="" bindload="" />
    // canvas 在上面显示,因为canvas在小程序中层级太高,所以需要转化为image src展示
    <canvas type="2d" id="canvas_{{listItem.id}}" style="position: fixed;top: -9999px;left: -9999px;width: 240rpx;height: 240rpx;"></canvas>
    <block>
      <image class="img-shape-main" src="{{ imgPath }}" mode="aspectFit" lazy-load="false" binderror="" bindload=""></image>
    </block>
    // 加一层背景模糊
    <view class="img-shape-bg" style="background-image: radial-gradient( ellipse at top, {{ listItem.style }} 50%, transparent, transparent)"></view>
  </view>
Component({
  properties: {
    listItem: {
      type: Object,
      default: {
        //shape: 0,
        //style: red,
      }
    }
  },
  observers: {
    'listItem': function(item) {
      let shape = item.shape
      var shapeImage = ''
      switch (shape) {
        case '0':
          shapeImage = 'pyramid'
          break
        case '1':
          shapeImage = 'cube'
          break
        case '2':
          shapeImage = 'diamond'
          break
        case '3':
          shapeImage = 'hexsphere'
          break
        case '4':
          shapeImage = 'icosphere'
          break
      }
      // 水晶图片是根据接口数据显示的
      let src = `shape/${shapeImage}_${item.style}@2x.png`
      this.setData({
        kindShape: src
      })
    }
  },
  data: {
    imgPath: ''
  },
  attached() {
    let item = this.data.listItem
    this.mergeImagesMiniProgram(item.id, '/assets/images/mask@2x.png', item.photos[0])
  },
  methods: {
    mergeImagesMiniProgram(id, maskPath, filePath) {
    let that = this
    let query = wx.createSelectorQuery().in(this)
        query.select(`#canvas_${id}`).fields({ node: true, size: true }).exec((rescanvas) => {
        let canvas = rescanvas[0].node
        let ctx = canvas.getContext('2d')
        const overlay = canvas.createImage()
              overlay.src = maskPath
        let width = canvas.width = 120
        let height = canvas.height = 120

        let canvasRatio = width / height
        overlay.onload = function () {
          ctx.drawImage(overlay, 0, 0, width, height)
          const template = canvas.createImage()
          template.src = filePath

          template.onload = function () {
            let realWidth = template.width
            let realHeight = template.height
            // cover
            let imgRatio = realWidth / realHeight
            let sw = 0, sh = 0, sx = 0, sy = 0;
            if(imgRatio <= canvasRatio) {
              sw = realWidth
              sh = sw / canvasRatio
              sx = 0
              sy = (realHeight - sh) / 2
            } else { 
              sh = realHeight
              sw = sh * canvasRatio
              sx = (realWidth - sw) / 2 
              sy = 0
            } 
            // ctx.globalCompositeOperation = "destination-in";
            ctx.globalCompositeOperation = "source-in";
            ctx.drawImage(template, sx, sy, sw, sh, 0, 0, width, height)
            // ctx.drawImage(template, 0, 0, width, height);
            wx.canvasToTempFilePath({
              canvas,
              width: width,
              height: height,
              destWidth: width * app.globalData.pixelRatio,
              destHeight: height * app.globalData.pixelRatio,
              success: async function(res) {
                let filePath = res.tempFilePath;
                that.setData({
                  imgPath: filePath
                })
              },
              fail: function (res) {
              }
            })
          }
        }
    })
  }
})

素材图片

水晶

蒙层

我不叫胖虎

有需要可+qq:2091545890

参考文档-canvas drawImage绘图实现contain和cover的效果
参考文档-CanvasRenderingContext2D

posted @ 2024-09-09 10:44  lisashare  阅读(5)  评论(0编辑  收藏  举报