文章目录

  • 前言
  • 一、普通二维码生成
  • 二、生成海报图片并保存
  • 1、引入依赖组件
  • 2、生成海报图片
  • 2.1 配置painter海报json
  • 2.2 引入数据构造函数并在适当时间触发生成海报
  • 2.3 弹窗组件中直接使用painter组件
  • 三、源码


前言

最近开发的小程序都有分享需求,功能大体为点击分享按钮,或主动生成海报后,用户操作保存对应海报为图片实现分享。以下是具体实现。

最终生成效果如图:



一、普通二维码生成

此处主要是生成工具库来生成普通二维码,如果要生成小程序码,只能通过后台接口调用开放API实现,且需要小程序已上线发布才能通过微信扫一扫进入小程序(废话不多说了。。。。)

  • 工具库:weapp.qrcode.min.js

template

<template>
	<view class="page">
		<canvas
          id="canvas"
          style="width: 160rpx; height: 160rpx"
          canvas-id="canvas"
        ></canvas>
	</view>
</template>

script

// 调用很简单
const drawQrcode = require("@/lib/weapp.qrcode.min.js)
export default {
	data(){return {}};
	onLoad(){
		this.drawCode();
	}
	methods:{
		drawCode(){
			drawQrcode({
				text: `二维码内容`,
				canvasId: "canvas",
				width: 80,
				height: 80
			})
			console.log("二维码生成")
		}
	}
}

二、生成海报图片并保存

生成海报分为两步
一是海报预览弹窗(这个是小程序内页弹窗所以直接代码编写就好)
二是通过painter将元素生成为图片保存到本地。以下代码只描述重点部分,全部代码请查看源码文件。
这里用到了painter这个组件库,这是一个通过配置json就直接生成图片的工具库,开源地址

1、引入依赖组件

这里引用了painter组件来生成(懒人不想造轮子- _ -)。因为是微信原生编写,所以放在根目录下的wxcomponents中

目录如下:

|__ wxcomponents
	|__ painter
		|__ painter.wxml
		|__ painter.wxss
		|__painter.json
		|__painter.js
		|__ ...

page.json中引入微信组件

{
	"globalStyle":{
		"usingComponents": {
			"painter": "/wxcomponents/painter/painter"
		 }
	}
}

2、生成海报图片

这里新建了两个文件,一个share.vue用于在小程序中直接展示海报的样式,一个share.js是配置painter海报的数据源

  • share.vue
<template>
  <view class="share" v-if="visible">
    <view class="share-content">
      <view class="share-chart-wrap">
        <image
          class="share-chart"
          @longpress="saveImgNow"
          src="http://qiniu.kingdou.fun/kingdou1.jpeg"
          mode="widthFix"
        />
        <view class="share-title">这是每报标题</view>
      </view>
      <view class="share-card">
        <view class="content-block">
          <view class="content-row">
            <view class="content-label">海报时间</view>
            <view class="content-value">{{ data.time }}</view>
          </view>
          <view class="content-row">
            <view class="content-label">描述</view>
            <view class="content-value">{{ data.introduce }}</view>
          </view>
          <view class="content-row">
            <view class="content-label">比赛说明</view>
            <view class="content-value">限定时间内根据动作次数记分</view>
          </view>
        </view>
        <view class="qr-block">
          <view class="qr-tips">
            <view class="tips-title">长按图片进行保存或者转发</view>
            <view class="tips-content"
              >扫描二维码即可参赛~<br />快去分享吧~
            </view>
          </view>
          <slot name="qrcode" @longpress="init"></slot>
        </view>
      </view>
    </view>
    <uni-icons
      class="close-btn"
      size="50"
      type="close"
      color="#fff"
      @click="close"
    />
    <painter
      v-show="isSave"
      style="position: absolute; top: 79rpx; left: 60rpx"
      :palette="template"
      @imgOK="onImgOK"
      @imgErr="onImgErr"
    />
  </view>
</template>

<script>
	import Card from "./share";

	export default {
		props: {
			visible: {
				type: Boolean,
				default: false,
			},
			data: {
				type: Object,
				default: () => {
					return {
						time: "",
						introduce: "",
					};
				},
			},
		},
		data() {
			return {
				imagePath: "",
				template: "",
			};
		},
		methods: {
			saveImgNow() {
				let data = { ...this.data};
				this.template = new Card().palette(data);
			},
			saveImage() {
				if (this.imagePath && typeof this.imagePath === "string") {
					// 图片保存到本地
					wx.saveImageToPhotosAlbum({
						filePath: this.imagePath,
					});
					// 关闭分享弹窗
					setTimeout(() => {
						this.$emit("update:visible", false)
					}, 500)
				}
			},
			close() {
				this.$emit("close");
			},
			onImgOK(e) {
				this.imagePath = e.detail.path;
				this.saveImage(this.imagePath);
			},
			onImgErr() {
				console.log("保存图片失败");
			},
		},
	};
</script>
<style lang="scss" scoped>
样式内容省略,详见源码。。。
</style>
  • share.js (内容太多,这里就只截部分作为参考,全部内容请参考文末的源码包)PS:这个文件通过工具生成并不需要自己编写
export default class LastMayday {
    palette(data) { // 传入动态数据源  
      return {
        width: "320px",
        height: "430px",
        background: "#f1f1f1",
        views: [
          {
            type: "text",
            text: "海报时间",
            css: {
              color: "#373737",
              background: "rgba(0,0,0,0)",
              width: "60px",
              height: "15.819999999999999px",
              top: "202px",
              left: "40px",
              // 。。。。省略
            },
          },
            {
            type: "text",
            text: data.time, //这里是动态数据
            css: {
              color: "#0061D4",
              background: "rgba(0,0,0,0)",
              width: "181px",
              height: "15.819999999999999px",
              top: "202px",
           		// 。。。。省略
            },
          },
          // 。。。。。
          }
2.1 配置painter海报json

这里即是对上面的share.js来历进行说明。要生成图片,得有生成图片的规则,也就是painter的绘制数据源。这里也是通过第三方网站来可视化生成的,通过在这个网站中先绘制好海报的样式后,再一键生成json文件,最后放进代码中。

2.2 引入数据构造函数并在适当时间触发生成海报
  • 在本文示例中是通过长按图片来触发,当然也可以是用户直接点击按钮来触发
import Card from './share.js'
export default {
	// 。。。。省略。。。
	methods: {
	// 这里假设用户长按图片,触发此方法。即上面share.vue中, @longpress="saveImgNow"
	saveImgNow() {
		let data = { ...this.data};
		this.template = new Card().palette(data);
	},
	// 生成图片事件监听(在调用new Card()后会自动触发
	// 图片生成成功时
	onImgOK(e) {
		this.imagePath = e.detail.path;
		this.saveImage(this.imagePath);
	},
	// 图片生成失败时
	onImgErr() {
		console.log("保存图片失败");
	},
}
2.3 弹窗组件中直接使用painter组件
<painter style="position: absolute; top: -799999rpx; left: -999960rpx" :palette="template" @imgOK="onImgOK"
			@imgErr="onImgErr" />
  • 这里style中的内容是为了在视觉上隐藏海报(本质上是通过canvas再生成海报,所以需要此元素显示后再在canvas上绘制为图片)

三、源码

地址参见:https://gitee.com/sophie-code-box/share-poster