分享商品生成海报的需求技术点总结

分享商品生成海报的需求技术点总结

需求描述

商品详情页中,点击分享按钮,能生成一张可以保存到手机的商品海报图片,让用户保存到手机之后,可以通过微信或者别的聊天工具,发送这张图片到聊天中,达到分享的效果。

需求分析

整个海报的尺寸比例和外背景由设计提供的背景图来决定,上方的商品图需要根据进入的商品详情不同,而去获取不同的商品主图。右下方放置的二维码,需要根据所选的商品不同,由前端生成不同的分享地址来生成不同的二维码图片。

实现方案

我采用的方案是,先在页面上根据设计的设计图和尺寸比例,渲染出一个海报的 DOM 展现在页面上,然后根据这个 DOM 生成一张图片,再隐藏渲染的 DOM ,展示这张图片。

技术要点

二维码生成

采用的是 vue-qr 这个二维码生成库,生成没有什么太大的问题,唯一的问题在于识别上面。

由于生成的二维码图片在尺寸小于 100px 之后就无法被识别,所以让设计调整了整个海报的尺寸,从而使二维码尽可能的在各个机型上都保证能有 100px 左右的大小。

DOM 转图片

这一部分是整个需求的重点,也是难点,我会着重,细致的来记录整个解决问题,完成需求的过程。

Dom 转图片的这个功能,由于安卓和 iOS 的兼容性不同,所以我选择使用的两个库一个是 domtoimage 还一个是 html2canvas 。其中安卓使用 domtoimage,iOS 使用 html2canvas。

原理:

使用svg的一个特性,允许在<foreignobject>标签中包含任意的html内容。(主要是 XMLSerializer | MDN这个apidom转为svg
所以,为了渲染那个dom节点,你需要采取以下步骤:

  1. 递归 clone 原始的 dom 节点
  2. 获取 节点以及子节点 上的 computed style,并将这些样式添加进新建的style标签中(不要忘记了clone 伪元素的样式)
  3. 嵌入网页字体
  • 找到所有的@font-face
  • 解析URL资源,并下载对应的资源
  • base64编码和内联资源 作为 data: URLS引用
  • 把上面处理完的css rules全部都放进<style>中,并把标签加入到clone的节点中去
  1. 内嵌图片
  • 内联图片src 的url 进 <img>元素
  • 背景图片 使用 background css 属性,类似fonts的使用方式
  1. 序列化 clone 的 dom 节点 为 svg
  2. 将xml包装到<foreignobject>标签中,放入svg中,然后将其作为data: url
  3. 将png内容或原始数据作为uint8array获取,使用svg作为源创建一个img标签,并将其渲染到新创建的canvas上,然后把canvas转为base64
  4. 完成

知道原理之后,我们才能更好的知道一些出现在调用库实现中的一些问题是怎么产生的,并且如何去解决它

遇到的问题和解决方案

  1. DOM 中的图片,在转换之后为空白

由于图片是放在服务端上的,dom 在加载的时候需要一定的时间去下载资源,所以必须等待所有的资源加载完毕后,再进行转换,才不会导致图片资源转换后消失。

解决方法:让转换代码使用 setTimeout 等待一定时间,再进行转换操作。

  1. 安卓中使用 domtoimage 直接转换成 png 的图,会导致生成的图片被放大

解决方法:使用 toSvg 的方法,转换成 svg 的格式。

  1. 安卓手机中无法将 svg 格式的图片保存到手机

解决方法:进行 canvas 构造,将 svg 格式的图绘制在 canvas 上,最后再将该 canvas 转换成 png 的图。

  1. 生成的图片清晰度不够

最终图片的清晰度,其实是取决于第一步中 html 转换成的 canvas 的清晰度

参考 canvas 在高倍屏下变模糊的处理方法和 canvas 绘制图片模糊的解决方法,我们知道其基本原理就是先将 canvas 放大为 设备的物理像素分辨率与CSS像素分辨率的比率(devicePixelRatio) 倍,最后通过 CSS 将 canvas 的 width 和 height 设置为原来的 1 倍大小。

实际在我们的项目中,做出以上优化之后,大颗粒一般的渲染结果还是会显得粗糙,所以我们需要再进行以下的优化

  1. 关闭 canvas 默认的抗锯齿设置,来实现图像的锐化
  2. 对于 dom 中的其他元素,也可以使用 css 样式的 scale 来实现同样的缩放

代码

    getPoster() {
      const self = this;
      const toast = self.$toast.loading({
        duration: 0,
        forbidClick: true,
        message: '正在生成海报...',
      });
      setTimeout(() => {
        const width = self.$refs.posterArea.offsetWidth;
        const height = self.$refs.posterArea.offsetHeight;
        const scale = window.devicePixelRatio;
        // eslint-disable-next-line no-console
        console.log(self.chargeUserAgent());
        if (self.chargeUserAgent() !== 'ios') { // 判断设备
          domtoimage.toSvg(self.$refs.posterArea, { cacheBust: true })
            .then((dataUrl) => {
              const canvas = document.createElement('canvas');
              canvas.width = width * scale;
              canvas.height = height * scale;
              const image = new Image();
              image.src = dataUrl;
              image.width = width * scale;
              image.height = height * scale;
              // 将基础图像进行放大
              const ctx = canvas.getContext('2d');
              image.onload = () => {
                ctx.drawImage(image, 0, 0, width * scale, height * scale); // 绘制到画布上
                self.posterImage = canvas.toDataURL('image/png');
              };
              toast.clear();
            })
            .catch((error) => {
              // eslint-disable-next-line no-console
              console.error('oops, something went wrong!', error);
            });
        } else {
          const canvas = this.createBaseCanvas(scale, width, height);
          html2canvas(self.$refs.posterArea, {
            useCORS: true,
            scrollY: 0,
            scrollX: 0,
            width,
            height,
            logging: false,
            scale: 1,
            canvas,
          }).then((canva) => {
            try {
              self.posterImage = canva.toDataURL('image/png');
              toast.clear();
            } catch (err) {
              // eslint-disable-next-line no-console
              console.log(err);
            }
          });
        }
      }, 3000);
    },
    createBaseCanvas(scale, width, height) { // 构造 canvas
      const canvas = document.createElement('canvas');
      canvas.width = width * scale;
      canvas.height = height * scale;
      const context = canvas.getContext('2d');
      context.mozImageSmoothingEnabled = false;
      context.webkitImageSmoothingEnabled = false;
      context.msImageSmoothingEnabled = false;
      context.imageSmoothingEnabled = false;
      context.scale(scale, scale);
      return canvas;
    },
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
生成商品海报分享图可以通过利用HTML5的Canvas和JavaScript来实现。以下是一个简单的实现步骤: 1. 在HTML页面中添加一个canvas元素,用于绘制海报。 ```html <canvas id="posterCanvas"></canvas> ``` 2. 使用JavaScript获取canvas元素,并设置其宽度和高度。 ```javascript // 获取canvas元素 var canvas = document.getElementById("posterCanvas"); // 设置canvas宽度和高度 canvas.width = 600; canvas.height = 800; ``` 3. 使用JavaScript绘制海报的背景图和商品图片。 ```javascript // 获取canvas的上下文对象 var ctx = canvas.getContext("2d"); // 绘制背景图 var bgImg = new Image(); bgImg.src = "bg.jpg"; bgImg.onload = function() { ctx.drawImage(bgImg, 0, 0, canvas.width, canvas.height); } // 绘制商品图片 var productImg = new Image(); productImg.src = "product.jpg"; productImg.onload = function() { ctx.drawImage(productImg, 50, 50, 500, 500); } ``` 4. 在海报上添加商品标题、价格等信息。 ```javascript // 绘制商品标题 ctx.font = "32px Arial"; ctx.fillStyle = "#000000"; ctx.textAlign = "center"; ctx.fillText("商品标题", canvas.width/2, 600); // 绘制商品价格 ctx.font = "24px Arial"; ctx.fillStyle = "#FF0000"; ctx.textAlign = "center"; ctx.fillText("¥99.00", canvas.width/2, 650); ``` 5. 最后,将canvas转换为图片,可供用户下载或分享。 ```javascript // 将canvas转换为图片 var imgData = canvas.toDataURL("image/png"); // 创建一个下载链接,让用户下载或分享海报 var link = document.createElement("a"); link.download = "poster.png"; link.href = imgData; link.click(); ``` 以上是一个简单的生成商品海报分享图的实现步骤,你可以根据自己的需求进行更改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值