uniapp 分享 绘制海报
小程序分享海报,由于微信的限制,暂时无法直接分享小程序到朋友圈(虽然微信开发者工具基础库从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 分享 绘制海报的更多相关文章
- uniapp分享功能-系统分享
uni-app分享 uniapp官网地址:https://uniapp.dcloud.io/api/plugins/share?id=sharewithsystem 调用系统分享组件发送分享消息,不需 ...
- 微信小程序之canvas绘制海报分享到朋友圈
绘制canvas内容 首先,需要写一个canvas标签,给canvas-id命名为shareBox <canvas canvas-id="shareBox"></ ...
- 利用Python快速绘制海报级别地图
1 简介 基于Python中诸如matplotlib等功能丰富.自由度极高的绘图库,我们可以完成各种极富艺术感的可视化作品,关于这一点我在系列文章在模仿中精进数据可视化中已经带大家学习过很多案例了. ...
- 使用Canvas绘制分享海报
这几天接到一个需求,需要将一个邀请链接转换为一个带有二维码并且能够分享出去的海报图,网上找了很多的方法,也踩了不少的坑,希望大家遇到类似的需求能够少走弯路.. 具体效果图如下: 效果图 首先我采用了 ...
- 开源)嗨,Java,你可以生成金山词霸的二维码分享海报吗?
As long as you can still grab a breath, you fight.只要一息尚存,就不得不战. 有那么一段时间,我特别迷恋金山词霸的每日一句分享海报.因为不仅海报上的图 ...
- 微信小程序利用canvas生成海报分享图片
一 . 效果 这是借用女神照生成的分享的海报,图片来自网络. 新增了poster组件和更新图片自适应 二 . 准备 准备两张图片连接,最好是自己开发账号验证的https图片链接. 三 . 实现思路 其 ...
- Java生成微信分享海报【基础设计】
前言 微信后台生成海报一般都是一个模板写死,然后就完事了,过了不久让修改个模板,就又要看半天,还要考虑是否重新复制一份改一改,越来越多的重复代码,全在一个图片类里,然后就越来越乱.这两天用设计模式处理 ...
- 关于canvas合成分享图
最近在uni-app项目中遇到一个合成分享图的需求,其实最开始是用原生写法来做的,后台发现在PC端测试是可以的,但在APP模拟器中会出现问题,可能是因为两者的js环境不同吧,uni-app官网也说了这 ...
- fastposter发布1.4.2 跨语言的海报生成器
fastposter发布1.4.2 跨语言的海报生成器 fastposter发布1.4.2 跨语言的海报生成器,一分钟完成海报开发 future: 完善docker镜像 引入异步asyncio 升级p ...
- java画海报
package demotest; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Font; impor ...
随机推荐
- i春秋include
打开题目,发现它提示我们有个phpinfo.php,所以我们直接访问,没有什么特殊的发现,根据题目提示include,找到allow_url_include的信息 (ctrl+f直接进入网页搜索) 发 ...
- python 爬取豆瓣电影评论,并进行词云展示
python 爬取豆瓣电影评论,并进行词云展示 本文旨在提供爬取豆瓣电影<我不是药神>评论和词云展示的代码样例 1.分析URL 2.爬取前10页评论 3.进行词云展示 1.分析URL 我不 ...
- Dart开发服务端,我是不是发烧(骚)了?
前言 最近一段时间,我和我的团队开发了两个 APP. 客户端方面采用了 Flutter,方便跨平台. 服务端方面剑走偏锋,没有采用 php, pythod, java之类的,而是采用了与 Flutte ...
- 【Java】二分查找标准代码
太菜了..写不出正确的... 干脆放一个标准代码,之后参考 boolean BinarySearch(int[] m){ int l=0,r=m.length-1;//减1相当于数组两头(lr都能指到 ...
- jQuery中each与data
一:each(for循环) 1.each作用 for循环前面容器类型 将里面的元素交给后面的函数去处理 有了each,就无需自己写for循环了 2.格式 $(容器类型 数组 自定义对象).each(f ...
- 【RocketMQ】主从模式下的消费进度管理
在[RocketMQ]消息的拉取一文中可知,消费者在启动的时候,会创建消息拉取API对象PullAPIWrapper,调用pullKernelImpl方法向Broker发送拉取消息的请求,那么在主从模 ...
- 绿色版MySQL8.0.26安装流程
下载 5.7 8.0 官网 https://dev.mysql.com/downloads/mysql/ 国内镜像网站 https://developer.aliyun.com/mirror/ ...
- win7安装Anaconda+TensorFlow(cpu版)+配置PyCharm
本着不折腾不舒服斯基,好久没安装软件玩了.今天趁天气不错,安装下TensorFlow(cpu版)(因为没钱上GPU),首先在网上搜了下教程,原文出处: https://blog.csdn.net/u0 ...
- 常用 Git 命令行操作
本文记录了一些常用 Git 命令行操作的具体使用方式 git clone git clone REPOSITORY_URL 拉取仓库,并使用仓库名作为本地文件名 git clone REPOSITOR ...
- JSONObject 相关
/** * 将json转为对应实体类 */ public static Object jsonToJavaObj(String json, Class cs) { return jsonToJavaO ...