一、功能背景:后端返回一张二维码url和姓名,分开的两个字段。前端拿到两个字段后,要实现拼接姓名在二维码图片正下方,合并成新的图片,并点击保存下来,如下图:。
二、项目技术栈:vue3+vant3+vite
三、计划实现方式:
后端:拼接姓名再返回新的url返回给前端保存。
前端:使用canvas标签,将图片与文案放在canvas标签合并生成新的图片再保存
四:存在问题:
如果前端在处理时,先看以下代码:
html:
<template>
<div class="canvas-container">
<canvas id="myCanvas"></canvas>
</div>
<van-button @click="saveImg">保存合并的图片</van-button>
</template>
js:
<script setup lang="ts">
import { onMounted } from "vue";
// 初始化时设置canvas的大小以适应父元素
function resizeCanvas() {
let canvas:any = document.getElementById('myCanvas');
// 获取父元素的宽度
const containerWidth = canvas.parentElement.clientWidth;
// 设置canvas的宽度为父元素的宽度
canvas.width = containerWidth;
// 根据需要设置canvas的高度,这里假设我们想要保持一个固定的纵横比
const aspectRatio = 1 / 1; // 例如1:1的纵横比
canvas.height = containerWidth * aspectRatio;
}
function init() {
let canvas:any = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
// 创建一个新的Image对象
const img = new Image();
// 设置图片源(这里使用了一个占位符URL,你需要替换为实际的图片URL)
img.src = "https://img.yzcdn.cn/vant/cat.jpeg";//第三方资源图片
// img.src = "http://10.0.16.78:6688/worker/mb/v1/withdrawal_del.png";//我本地图片(项
// 目public下的图片)
// 当图片加载完成后,绘制图片和文本
img.onload = function() {
// 绘制图片
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// 设置文本内容、字体和位置
const text = '这是合并的文案';
const fontSize = 24;
const x = 100;
const y = canvas.height - 10 - fontSize;
// 设置字体样式
ctx.font = `${fontSize}px Arial`;
// 设置文本颜色
ctx.fillStyle = 'black';
// 绘制文本
ctx.fillText(text, x, y);
}
}
// 保存图片使用canvas的toDataURL方法将其内容转换为PNG格式的DataURL
function saveImg(){
let canvas:any = document.getElementById('myCanvas');
const link = document.createElement('a');
link.href = canvas.toDataURL('image/png');
link.download = 'merged_image.png';
link.click();
}
onMounted(() => {
resizeCanvas()
init()
})
</script>
css:
<style lang="less" scoped>
/* 设置canvas的宽度为100%,这样它会填满其父元素的宽度 */
canvas {
width: 100%;
/* 如果你希望canvas的高度自动缩放以保持图片的纵横比,
可以设置一个相对高度,或者通过JavaScript计算并设置 */
height: auto;
}
/* 你可以设置一个包含canvas的父元素,并给这个父元素设置宽度 */
.canvas-container {
width: 100%; /* 或者其他你想要的宽度 */
/* 可以设置max-width来限制canvas的最大宽度 */
max-width: 1000px; /* 例如 */
margin: 0 auto; /* 水平居中 */
}
</style>
以上代码看完后,合并展示效果是没有问题的,但是最终你在点击保存时,会报错无法保存,是因为跨域了
详细原因是:
当canvas的内容来自不同的源(即不同的域名、端口或协议)时,浏览器会出于安全考虑限制对其内容的访问。如果你尝试从这样的canvas中导出图片,会抛出一个安全错误。
link.href = canvas.toDataURL('image/png');
: 使用canvas
的toDataURL
方法将其内容转换为PNG格式的DataURL,并将这个DataURL赋值给link
元素的href
属性。DataURL是一个可以直接嵌入到HTML中的数据编码形式,包含了一个MIME类型(这里是image/png
)和编码后的数据。
五、解决方案:
- 确保所有资源都来自同一源:这是最简单的方法,但可能不是总可行的,尤其是当你依赖于第三方资源时。
- 设置CORS头:如果你可以控制canvas内容的源服务器,确保它返回正确的CORS头。这允许跨源请求,并允许canvas操作这些资源。
- 使用代理服务器:设置一个代理服务器来从其他源获取资源,并将它们作为同一源的资源返回给你的应用。
- 使用服务器端处理:在服务器端处理图像合并或操作,然后直接提供结果图片。
对于大多数web应用来说,最实际的方法可能第4种。确保你的服务器端资源配置正确,允许跨源请求,或者将图像处理逻辑移到服务器端。
其实,以上如果由前端处理,那方案本身就存在些问题的,一是图片是动态返回的,不是写死的资源,二是就算将图片转base64后合并导出图片,那也得考虑性能问题,图片资源大的情况也不可行。这是个人见解,如果有错误地方或更优的办法欢迎提出来!