前端学习交流QQ群:1群:173683895 ,2群: 173683866
承接项目开发,需求功能开发,博主微信号:Jay_09168
首先PASS掉两个实现方案:
方案1:微信浏览器为了安全考虑不支持执行使用代码直接保存图片到相册
方案2:不支持A标签下载
所以只能曲线救国,自己写一个预览图片组件,在预览的图片上面加上提示,长按保存/分享图片
看效果:
这是模仿uniapp中H5环境原生的预览图片组件样式,由于原生组件不支持在图片上层加自定义提示的文字,所以自己写一个简单的组件加上“长按保存/分享图片”提示文字。
直接上代码:
canvasImg 是图片地址,你自己添加或者由canvas画出来的都可以。canvas海报生成图片可以参考下面的页面代码
<view v-if="canvasImg" class="canvasBoom">
<image class="canvasImg" :src="canvasImg" mode="heightFix"></image>
<image class="clear" @click="canvasImg=''" src="/static/image/close.png" mode=""></image>
<view class="canvasTips">
长按保存/分享图片
</view>
</view>
CSS
.canvasBoom {
height: 100vh;
width: 100vh;
background: rgba(58, 58, 58, 0.5);
position: fixed;
left: 0;
top: 0;
z-index: 998;
display: flex;
.clear {
position: fixed;
top: 80rpx;
right: 40rpx;
width: 114.5rpx;
height: 100rpx;
z-index: 9999;
}
.canvasTips {
position: absolute;
bottom: 80rpx;
width: 100vw;
font-size: #333;
font-size: 26rpx;
text-align: center;
z-index: 999;
}
/* 水平居中 */
.canvasImg {
height: 100vh;
width: 50%;
position: relative;
margin-left: 12.5%;
}
}
页面代码,仅供参考
<template>
<view class="page">
<view v-if="canvasImg" class="canvasBoom">
<image class="canvasImg" :src="canvasImg" mode="heightFix"></image>
<image class="clear" @click="canvasImg=''" src="/static/image/close.png" mode=""></image>
<view class="canvasTips">
长按保存/分享图片
</view>
</view>
<!-- 高危因素弹出层 -->
<view class="shade" v-if="iFHighRisk&&content1">
<view class="alternative_bg_2">
</view>
<view class="model">
<view class="" v-if="content3">
<view class="title" v-if="!iFHighRisk2">高危因素</view>
<view class="title" v-if="iFHighRisk2">{{title}}</view>
</view>
<view class="" v-if="!content3">
<view class="title">{{title=='未见异常'||title=='阴性'?'高危因素':title}}</view>
</view>
<view class="centent1" v-if="iFHighRisk2">
<rich-text :nodes="content3"></rich-text>
</view>
<view class="centent1" v-if="!iFHighRisk2">
<rich-text :nodes="content1"></rich-text>
</view>
<view class="centent2">
<view class="" v-if="iFHighRisk2" v-for="item in content4">{{item}}</view>
<view class="" v-if="!iFHighRisk2" v-for="item in content2">{{item}}</view>
</view>
<image :src="home_backBtn" @click="iFHighRisk=false" class="home_backBtn" style="width: 220rpx;"
mode="widthFix"></image>
</view>
</view>
<!-- 参考文献 -->
<view class="shade" v-if="iFReference">
<view class="alternative_bg_2">
</view>
<view class="model">
<view class="title">文 献</view>
<view class="centent1" v-for="item in reference">{{item}}</view>
<image :src="home_backBtn" @click="iFReference=false" class="home_backBtn" style="width: 220rpx;"
mode="widthFix"></image>
</view>
</view>
<view class="posterWrap">
<view class="flex">
<image class="posterImg" :src="posterImg" mode="widthFix"></image>
<canvas canvas-id="posterCanvas" class="myCanvas"></canvas>
<view class="backIndex" :style="{bottom:isBottom ? '170rpx' : '180rpx'}">
<image class="back" :src="home_backBtn" mode="widthFix" @click="backIndexs"></image>
<!-- @click="click('see1')" -->
<image class="next" :src="saveCanvas" mode="widthFix" @click="handleSave"></image>
<!-- <image class="next" :src="saveCanvas" mode="widthFix" @click="handleSave"></image> -->
</view>
<view class="qrCode" @touchstart='timestart' @touchend='timeend'
:style="{bottom: isTopAge == '1940rpx' ? '440rpx' : '250rpx'}">
</view>
</view>
<view class="see1" v-if="!iFReference" @click="click('see1')">
</view>
<view class="see2" v-if="!iFReference" @click="iFReference=true" :style="{top: isTopAge}">
</view>
<view class="highRisk" v-if="!iFReference" @click="iFHighRisk=true,iFHighRisk2=false">
</view>
<view class="highRisk2" v-if="content3&&!iFReference" @click="iFHighRisk2=true,iFHighRisk=true">
</view>
<!-- 参考文献 -->
<view class="shade1" v-if="img != adviceImg">
<view class="alternative_bg_2">
</view>
<view class="model">
<image class="pageBg" :src="img" mode="widthFix"></image>
<view class="btn3" @click="click('btn3')">
</view>
</view>
</view>
</view>
</view>
</template>
<script>
var that;
import {
base64src
} from "@/util/base64src.js";
export default {
data() {
return {
canvasImg: '',
posterImg: '',
resultBg: 'https://xxx/resultBg.png',
home_backBtn: this.$config.cdn_url + 'home_backBtn.png',
home_subBtn: this.$config.cdn_url + 'home_downBtn.png?v=1',
saveCanvas: '../../../static/image/saveCanvas.png',
InfoSync: {},
thirdwx: "",
growTallId: '',
userInfo: {},
userToken: '',
list: [],
iFHighRisk: false,
iFHighRisk2: false,
title: '',
content1: '',
content2: '',
content3: '',
content4: '',
home_backBtn: this.$config.cdn_url + 'home_backBtn.png',
adviceImg: '',
referenceImg: '',
reference: [], //参考文献
iFReference: false,
processImg: '',
img: '',
img2: this.$config.cdn_url + 'pageBg2.jpg',
pageBgL: this.$config.cdn_url + 'pageBg2.jpg',
pageBgH: this.$config.cdn_url + 'pageBg3.jpg',
back: this.$config.cdn_url + 'back_1.jpg',
poster: this.$config.cdn_url + 'poster_1.png',
isHighRisk: null,
id: '',
ageId: '',
isBottom: true,
isTopAge: false,
}
},
onHide() {
console.log('英寸');
},
onLoad(option) {
this.$common.Init.call(this);
that = this;
this.posterImg = ''
console.log('option', option)
this.ageId = option.ageId;
if (option.id && option.id != 999) {
this.id = option.id;
this.getScreeningOptContent()
} else {
this.getContentNullRef(option.ageId)
}
// processImg 检查流程
// referenceImg 参考文献
this.adviceImg = uni.getStorageSync('adviceImg')
this.referenceImg = uni.getStorageSync('referenceImg')
this.processImg = uni.getStorageSync('processImg')
this.isHighRisk = uni.getStorageSync('isHighRisk')
let age = uni.getStorageSync('age')
if (age == '<25岁') {
this.isTopAge = '2080rpx'
} else if (age == '≥65岁') {
this.isTopAge = '1940rpx'
} else {
this.isTopAge = '2150rpx'
}
this.img = this.adviceImg;
let ids = [3, 13, 22]
let id = ids.find(item => item == this.id)
if (id) {
this.isBottom = true
} else {
this.isBottom = false
}
console.log(id);
},
onReady() {
this.getAnswerId()
},
methods: {
timestart(e) {
console.log('开始', e)
this.startTime = e.timeStamp
},
timeend(e) {
this.endTime = e.timeStamp
let times = this.endTime - this.startTime
if (times > 300) {
// 是否长按
console.log('长按大于300毫秒');
uni.showModal({
title: '提示',
content: `点击确定,进入疫百科\r\n了解更多疫苗相关知识`,
success(res) {
if (res.confirm) {
console.log('用户点击确定')
location.href = 'https://sss2&v=2022121401'
// uni.navigateTo({
// url: '/pages/pubPage/webView/webView'
// })
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
} else {
console.log('长按小于300毫秒');
}
console.log('结束', e)
},
// 获取所有答案列表
async getAnswerId() {
let data = await this.$request(this.$api.question.findAllByUserId, {
userId: uni.getStorageSync('token')
})
this.list = data.data
this.drawCanvas()
console.log(data);
},
drawCanvas() {
const InfoSync = uni.getSystemInfoSync();
this.InfoSync = InfoSync;
const screenWidth = InfoSync.screenWidth
console.log('screenWidth', screenWidth)
const ctx = uni.createCanvasContext("posterCanvas");
let bili = (InfoSync.windowWidth / 375) * 1;
console.log('bili', bili)
let intermediateLine = 260 * bili
// isupdate 等于1时,答案显示"无" distanceTop 高度 isHighRisk 是否高危因素
var str_filter = (idx, idxx, distanceTop) => {
if (idx == '无') {
}
var questionItem = this.list[idx].questionItems[idxx]
var str = questionItem.itemTitle;
if (this.list[idx].isupdate == 1) {
str = '无'
}
if (questionItem.isHighRisk == 1) {
ctx.fillStyle = "red";
ctx.font = "bold 11px Arial";
} else {
ctx.font = "normal 10px Arial";
ctx.fillStyle = "#000000";
}
if (str.length > 12) {
ctx.fillStyle = "red";
ctx.font = "bold 8px Arial";
// str = str.substring(0, 9) + '···';
}
ctx.fillText(str, intermediateLine, distanceTop * bili);
ctx.font = "normal 10px Arial";
ctx.fillStyle = "#000000";
}
this.resultBg = this.adviceImg
console.log('this.resultBg', this.resultBg);
this.saveThe(
this.resultBg,
(path) => {
console.log('path', path)
ctx.drawImage(path, 0, 0, InfoSync.windowWidth, 1459 *
bili); //ctx.drawImage(图片路径,距离画布左边,距离画布右边,图片宽,图片高
ctx.save(); // 保存当前绘画
ctx.textAlign = "center";
var distanceTop = 198; //距离顶部的距离
if (this.list[0].questionItems) {
str_filter(0, 0, distanceTop)
} else {
ctx.fillText('无', intermediateLine, distanceTop * bili);
}
distanceTop += 21
if (this.list[1].questionItems) {
str_filter(1, 0, distanceTop)
} else {
ctx.fillText('无', intermediateLine, distanceTop * bili);
}
distanceTop += 21
if (this.list[2].questionItems) {
str_filter(2, 0, distanceTop)
} else {
ctx.fillText('无', intermediateLine, distanceTop * bili);
}
distanceTop += 21.5
if (this.list[3].questionItems) {
str_filter(3, 0, distanceTop)
} else {
ctx.fillText('无', intermediateLine, distanceTop * bili);
}
distanceTop += 21.5
if (this.list[4].questionItems) {
str_filter(4, 0, distanceTop)
} else {
ctx.fillText('无', intermediateLine, distanceTop * bili);
}
distanceTop += 21
if (this.list[5].questionItems) {
str_filter(5, 0, distanceTop)
} else {
ctx.fillText('无', intermediateLine, distanceTop * bili);
}
distanceTop += 21
if (this.list[6].questionItems) {
str_filter(6, 0, distanceTop)
} else {
ctx.fillText('无', intermediateLine, distanceTop * bili);
}
distanceTop += 21
if (this.list[7].questionItems) {
str_filter(7, 0, distanceTop)
} else {
ctx.fillText('无', intermediateLine, distanceTop * bili);
}
distanceTop += 22
if (this.list[8].questionItems) {
str_filter(8, 0, distanceTop)
} else {
ctx.fillText('无', intermediateLine, distanceTop * bili);
}
console.log('distanceTop - 368', distanceTop)
// 多选题
// 近1年生殖道感染情况
// ctx.fillText("近1年生殖道感染情况", intermediateLine, 564 * bili);
// ctx.fillText("近2年生殖道感染情况", intermediateLine, 580 * bili);
// ctx.fillText("近3年生殖道感染情况", intermediateLine, 596 * bili);
// ctx.fillText("...", intermediateLine, 612 * bili);
if (this.list[9].questionItems) {
if (this.list[9].questionItems.length > 3) {
distanceTop += 20
str_filter(9, 0, distanceTop)
distanceTop += 16
str_filter(9, 1, distanceTop)
distanceTop += 16
str_filter(9, 2, distanceTop)
distanceTop += 10
ctx.fillText("...", intermediateLine, distanceTop * bili);
}
if (this.list[9].questionItems.length == 3) {
distanceTop += 24
str_filter(9, 0, distanceTop)
distanceTop += 16
str_filter(9, 1, distanceTop)
distanceTop += 16
str_filter(9, 2, distanceTop)
}
if (this.list[9].questionItems.length == 2) {
distanceTop += 34
str_filter(9, 0, distanceTop)
distanceTop += 16
str_filter(9, 1, distanceTop)
}
if (this.list[9].questionItems.length == 1) {
distanceTop += 40
// 586 - 408 = 178
str_filter(9, 0, distanceTop)
}
} else {
distanceTop += 40
ctx.fillText('无', intermediateLine, distanceTop * bili);
}
// 是否伴有一下体征
// ctx.fillText("是否伴有一下体征1", intermediateLine, 631 * bili);
// ctx.fillText("是否伴有一下体征2", intermediateLine, 647 * bili);
// ctx.fillText("是否伴有一下体征3", intermediateLine, 663 * bili);
// ctx.fillText("...", intermediateLine, 676 * bili);
distanceTop = 430;
if (this.list[10].questionItems) {
if (this.list[10].questionItems.length > 3) {
distanceTop += 20
str_filter(10, 0, distanceTop)
distanceTop += 16
str_filter(10, 1, distanceTop)
distanceTop += 16
str_filter(11, 2, distanceTop)
distanceTop += 10
ctx.fillText("...", intermediateLine, distanceTop * bili);
}
if (this.list[10].questionItems.length == 3) {
distanceTop += 24
str_filter(10, 0, distanceTop)
distanceTop += 16
str_filter(10, 1, distanceTop)
distanceTop += 16
str_filter(11, 2, distanceTop)
}
if (this.list[10].questionItems.length == 2) {
distanceTop += 34
str_filter(10, 0, distanceTop)
distanceTop += 16
str_filter(10, 1, distanceTop)
}
if (this.list[10].questionItems.length == 1) {
distanceTop += 40
str_filter(10, 0, distanceTop)
}
} else {
distanceTop += 40
ctx.fillText('无', intermediateLine, distanceTop * bili);
}
console.log('distanceTop - 586', distanceTop)
// 是否伴有以下情况
// 字太长,截取掉
var strTntercept = (idx, distanceTop) => {
str_filter(11, idx, distanceTop)
}
distanceTop = 492
if (this.list[11].questionItems) {
if (this.list[11].questionItems.length > 3) {
distanceTop += 20
strTntercept(0, distanceTop)
distanceTop += 16
strTntercept(1, distanceTop)
distanceTop += 16
strTntercept(2, distanceTop)
distanceTop += 10
ctx.fillText("...", intermediateLine, distanceTop * bili);
}
if (this.list[11].questionItems.length == 3) {
distanceTop += 24
strTntercept(0, distanceTop)
distanceTop += 16
strTntercept(1, distanceTop)
distanceTop += 16
strTntercept(2, distanceTop)
}
if (this.list[11].questionItems.length == 2) {
distanceTop += 34
strTntercept(0, distanceTop)
distanceTop += 16
strTntercept(1, distanceTop)
}
if (this.list[11].questionItems.length == 1) {
distanceTop += 40
strTntercept(0, distanceTop)
}
} else {
distanceTop += 40
ctx.fillText('无', intermediateLine, distanceTop * bili);
}
console.log('distanceTop', distanceTop)
distanceTop = 675
ctx.save();
// ctx.draw();
ctx.draw(true, () => {
uni.canvasToTempFilePath({
// x: 0,
// y: 0,
// width: 750,
// height: 2502,
// destWidth: 750*2.5,
// destHeight: 2502*2.5,
canvasId: 'posterCanvas',
fileType: "png",
quality: 1,
success(res) {
console.log('绘制成功-------', res)
that.posterImg = res.tempFilePath
},
fail(err) {
console.log('绘制失败', err)
}
});
});
}
);
},
// 小程序需要将图片下载下来,然后才能绘制到画布上
saveThe(url, callback) {
uni.getImageInfo({
src: url, // 服务器返回的图片地址
success: (res) => {
callback(res.path);
},
fail: function(res) {},
});
},
// 返回
backIndexs() {
// uni.navigateBack({
// delta: 1,
// })
uni.navigateTo({
url: '/pages/pubPage/alternative/alternative?id=' + this.ageId
})
},
// 点击保存时,将画布生成海报
handleSave() {
var that = this;
uni.showLoading({
title: "正在保存...",
mask: true,
});
console.log('保存图片');
that.saveImg();
},
saveImg() {
var that = this
// 按照设备比例去计算图片和画布尺寸
let bili = (this.InfoSync.windowWidth / 375) * 1;
uni.canvasToTempFilePath({
x: 0,
y: 0,
width: 375 * bili,
height: 1459 * bili,
destWidth: 375 * bili * this.InfoSync.pixelRatio,
destHeight: 1459 * bili * this.InfoSync.pixelRatio,
fileType: "png",
quality: 1,
canvasId: "posterCanvas",
success: function(res) {
uni.hideLoading();
var tempFilePath = res.tempFilePath;
that.canvasImg = tempFilePath
// uni.previewImage({
// urls: [tempFilePath],
// });
// uni.showToast({
// title:'长按保存/分享图片',
// icon:'none',
// duration:5000
// })
// 创建下载链接
// var link = document.createElement('a');
// link.href = tempFilePath;
// link.download = 'canvas_image.png'; // 设置下载文件名
// // 模拟点击下载链接
// link.click();
// location.href = tempFilePath
},
fail: (err) => {
uni.hideLoading();
uni.showToast({
title: "出现了错误,请稍后再试",
icon: "none",
});
},
});
},
getContentNullRef(id) {
if (id == 1) id = 36;
if (id == 4) id = 37;
this.$request(this.$api.screening.getContentNullRef, {
id: this.id || id
}, 'get').then(res => {
this.reference = res.data.reference
this.content1 = res.data.content1
this.content2 = res.data.content2
if (res.data.content3) {
this.content3 = res.data.content3
this.content4 = res.data.content4
}
console.log('reference1', this.reference)
})
},
getScreeningOptContent() {
this.$request(this.$api.screening.getScreeningOptContent, {
id: this.id
}, 'get').then(res => {
this.title = res.data.title || ''
this.reference = res.data.reference
this.content1 = res.data.content1
this.content2 = res.data.content2
if (res.data.content3) {
this.content3 = res.data.content3
this.content4 = res.data.content4
}
console.log('reference2', this.reference)
})
},
click(e) {
if (e == 'highRisk') {
this.img = this.isHighRisk
this.img2 = this.pageBgH
}
if (e == 'see1') {
this.img = this.referenceImg
this.img2 = this.pageBgH
} else if (e == 'btn3') {
this.img = this.adviceImg
this.img2 = this.pageBgL
} else if (e == 'btn1') {
console.log('aaaaa')
uni.reLaunch({
url: '../index/index'
})
} else if (e == 'btn2') {
console.log('保存图片')
console.log('保存图片')
uni.showLoading({
title: '图片生成中'
})
var link = document.createElement('a');
//把a标签的href属性赋值到生成好了的url
link.href = this.img;
//通过a标签的download属性修改下载图片的名字
// link.download = '小票信息.png';
// link.download = imgPath.replace(/(.*\/)*([^.]+.*)/ig,"$2").split("?")[0];
//让a标签的click函数,直接下载图片
link.click();
uni.hideLoading()
// uni.downloadFile({
// url: this.img,
// success: function(res) {
// console.log(res);
// uni.saveFile({
// filePath: res.tempFilePath,
// success: function(data) {
// uni.showToast({
// title: "保存成功",
// icon: "success",
// duration: 2000
// });
// },
// fail: function(err) {
// console.log(err);
// },
// complete(res) {
// console.log(res);
// }
// });
// }
// });
}
}
}
}
</script>
<style lang="scss" scoped>
.page {
height: 2918rpx;
width: 100vw;
}
.canvasBoom {
height: 100vh;
width: 100vh;
background: rgba(58, 58, 58, 0.5);
position: fixed;
left: 0;
top: 0;
z-index: 998;
display: flex;
.clear {
position: fixed;
top: 80rpx;
right: 40rpx;
width: 114.5rpx;
height: 100rpx;
z-index: 9999;
}
.canvasTips {
position: absolute;
bottom: 80rpx;
width: 100vw;
font-size: #333;
font-size: 26rpx;
text-align: center;
z-index: 999;
}
/* 水平居中 */
.canvasImg {
height: 100vh;
width: 50%;
position: relative;
margin-left: 12.5%;
}
}
// 弹出层
.shade1 {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
z-index: 101;
.model {
width: 600rpx;
height: 800rpx;
position: relative;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
.pageBg {
position: absolute;
width: 600rpx;
left: 50%;
transform: translateX(-50%);
top: 70rpx;
z-index: 2;
}
.btn3 {
left: 50%;
height: 70rpx;
width: 220rpx;
transform: translateX(-50%);
z-index: 9999;
position: absolute;
bottom: 0;
}
}
}
.centent {
position: relative;
top: 70rpx;
z-index: 3;
width: 100vw;
}
// 弹出层
.shade {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
z-index: 101;
.model {
width: 600rpx;
height: 800rpx;
position: relative;
left: 50%;
top: 50%;
background-color: #fff;
border-radius: 20rpx;
transform: translate(-50%, -50%);
.title {
color: #035361;
width: 100%;
text-align: center;
padding-top: 70rpx;
font-weight: 600;
font-size: 44rpx;
}
.centent1 {
color: #035361;
font-size: 22rpx;
width: 90%;
margin-left: 5%;
margin-top: 40rpx;
text-align: justify;
}
.centent2 {
color: #035361;
font-size: 18rpx;
width: 90%;
margin-left: 5%;
top: 450rpx;
position: absolute;
view {
margin-bottom: 6rpx;
}
}
.home_backBtn {
max-height: 100rpx;
bottom: 70rpx;
left: 50%;
transform: translateX(-50%);
position: absolute;
}
}
}
.see1 {
position: absolute;
top: 1662rpx;
right: 110rpx;
height: 160rpx;
width: 100rpx;
z-index: 9999;
}
.see2 {
position: absolute;
top: 2150rpx;
right: 90rpx;
height: 80rpx;
width: 100rpx;
}
.btn1 {
position: absolute;
top: 1110rpx;
left: 130rpx;
height: 100rpx;
width: 240rpx;
z-index: 2;
}
.btn2 {
position: absolute;
top: 1110rpx;
right: 130rpx;
height: 100rpx;
width: 230rpx;
z-index: 2;
}
.highRisk {
position: absolute;
top: 1662rpx;
left: 100rpx;
height: 160rpx;
width: 100rpx;
z-index: 999;
}
.highRisk2 {
position: absolute;
top: 1662rpx;
left: 220rpx;
height: 160rpx;
width: 120rpx;
}
.posterWrap {
width: 750rpx;
// min-height: 100vh;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.flex {
width: 750rpx;
height: 2918rpx;
position: relative;
}
.posterImg {
width: 750rpx;
border-radius: 10rpx;
}
.myCanvas {
position: absolute;
// left: 110vw;
top: -3000rpx;
width: 750rpx;
height: 2918rpx;
border-radius: 10rpx;
overflow: hidden;
}
.save {
width: 260rpx;
height: 60rpx;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 128rpx;
z-index: 9999;
}
}
.backIndex {
width: 100%;
position: absolute;
bottom: 200rpx;
z-index: 2;
display: flex;
justify-content: center;
.back {
width: 230rpx;
height: 60rpx;
}
.next {
width: 230rpx;
height: 60rpx;
margin-left: 40rpx;
}
}
.qrCode {
width: 100%;
height: 400rpx;
position: absolute;
// border: 1px solid red;
bottom: 250rpx;
z-index: 2;
}
</style>