微信H5生成海报图片并保存到手机

一、背景

微信活动H5的最后一页,永远是雷打不动的——长按保存海报到手机。

二、解决思路

为啥非得长按保存图片,而不弄个“点击按钮保存到手机”呢?这是因为微信浏览器作了一些安全限制,不支持通过脚本保存图片到本地。那么就只能使用微信浏览器自己的保存图片功能,长按保存了。

解决思路如下:

1、先用CSS、html排版好海报样式;

2、使用 html2canvas 库将 DOM 转化成图片;

3、展示图片。

三、解决方法

(本示例使用Vue3 + TS)

1、先用CSS、html排版好海报样式

用CSS和html根据设计稿排好版,并用一个容器(如div)把所有海报内容包起来。这里使用的Vue3,加上了ref属性方便后面引用这个DOM元素。

<div class="container" ref="containerDom">
  <div>我是海报</div>
  <img src="..." />
  <div class="button-group">一些按钮</div>
</div>

2、使用 html2canvas 库将 DOM 转化成图片

需要使用到 html2canvas 这个库,官网:html2canvas - Screenshots with JavaScript。官网介绍十分简短,简短到我看完还是不知道怎么用。。所以推荐先看下面的教程,再看官方文档来查配置项。

1)项目安装依赖:

// 使用 npm:
npm install html2canvas --save

// 使用 yarn:
yarn add html2canvas

2)在需要的地方引入:

import html2canvas from 'html2canvas';

3)编写生成海报的函数:

/**
  * @params containerDom: 包裹待转化的DOM,
  * 即本例中的 <div class="container" ref="containerDom">
  */
const makePoster = async (containerDom: HTMLElement | null | undefined) => {
  let posterUrl = '';
  if (!containerDom) {
    console.error('containerDom为空: ');
    return posterUrl;
  }

  // 设置配置项,可以在官方文档查
  const options = {
    scale: window.devicePixelRatio, // 放大倍数,消除截图锯齿
    width: gradesDom?.offsetWidth, // 生成的图片宽度,单位px。【gradesDom?.offsetWidth】是本人项目中原html元素的宽度,换成你想要的宽度即可
    height: posterDom.offsetHeight + 60, // 生成的图片高度,单位px。【posterDom.offsetHeight + 60】是本人项目中容纳img的元素的高度,换成你想要的高度即可
    backgroundColor: null, // 设置背景色,null表示透明。若不设置此项,默认是白色背景
  };
  try {
    const canvas = await html2canvas(containerDom, options);
    posterUrl = canvas.toDataURL('image/png');
  } catch (err) {
    console.info('制作海报出错了:', err);
  }
  return posterUrl;
};

注:这里若生成图片失败,可以检查一下是否containerDom为空、containerDom中是否使用了 html2canvas 不支持的 css 属性(查看官方文档)。

3、展示图片

在合适的时候(如进入页面时或点击某按钮后)调用在上一步编写的函数,我们就能拿到图片的Url,就可以为所欲为啦~ 只需用一个 img 元素,设置其 src 为该 url 即可。

<!-- vue中,记得在src前加上冒号":"才是绑定变量 -->
<img :src="posterUrl" />

4、完整代码

仅作示意

<template>
  <div class="container" ref="containerDom">
    <div>我是海报</div>
    <img src="..." />
    <div class="button-group">
      <button @click="makePoster">生成海报</button>
    </div>
  </div>
  <img :src="posterUrl" />
</template>

<script lang="ts">
import html2canvas from 'html2canvas';
import { defineComponent, ref } from 'vue'; // vue3 + ts 才会用到

export default defineComponent({
  setup() {
    const containerDom = ref(null);
    const makePoster = async (containerDom: HTMLElement | null | undefined) => {
      let posterUrl = '';
      if (!containerDom) {
        console.error('containerDom为空: ');
        return posterUrl;
      }

      const options = {
        scale: window.devicePixelRatio,
        width: gradesDom?.offsetWidth, // 【gradesDom?.offsetWidth】是本人项目中原html元素的宽度,换成你想要的宽度即可
        height: posterDom.offsetHeight + 60, // 【posterDom.offsetHeight + 60】是本人项目中原html元素的高度,换成你想要的高度即可
        backgroundColor: null,
      };
    
      try {
        const canvas = await html2canvas(containerDom, options);
        posterUrl = canvas.toDataURL('image/png');
      } catch (err) {
        console.info('制作海报出错了:', err);
      }
      return posterUrl;
    };

    // vue3 setup的return
    return {
      containerDom, // 这个一定记得return,不然永远是空!
      makePoster,
      posterUrl,
    };
  },
});

</script>

四、补充

有一个比较常用的 html2canvas 配置项:ignoreElements。可以用来过滤掉 containerDom 中不想到图片里的内容,如一些操作按钮。

其是一个返回值为 boolean 的函数,会应用到 containerDom 的每个后代元素上。html2canvas 在生成 canvas 前,会把 containerDom 深拷贝一份。若对某后代元素执行 ignoreElements 时返回值是 true,则该元素不会被拷贝,自然也不会出现在最后的图片中。

const makePoster = async (containerDom: HTMLElement | null | undefined) => {
  // ...

  const options = {   
    // element 是 containerDom 的后代元素
    // 这里类名包含 “button-group” 的元素将不会出现在生成的图片中
    ignoreElements: (element: Element) => {
      const isButtons = element.classList.contains('button-group');
      return isButtons;
    },
  }

  // ...
};

有不正确之处还望指出~

  • 8
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值