使用canvas 自定义海报 (自定义二维码位置、实时预览)
生成海报:1、上传海报样式;2、自定义海报二维码位置、大小;3、实时预览
MakePoster.vue 组件代码
1 <template> 2 <div> 3 <a-row> 4 <a-col :span="12"> 5 <a-form-model 6 ref="ruleForm" 7 :model="form" 8 :rules="rules" 9 :label-col="labelCol" 10 :wrapper-col="wrapperCol" 11 > 12 <a-form-model-item 13 label="分享卡片" 14 prop="img" 15 extra="建议宽度:不小于 750px,png/jpg格式,2M以内" 16 > 17 <upload-file 18 @uploadPic="uploadImg" 19 :img="form.img" 20 :imgWidth="320 / 2" 21 ></upload-file> 22 </a-form-model-item> 23 <a-form-model-item 24 label="二维码定位" 25 prop="posX" 26 extra="X 代表二维码左顶端的横坐标,正数,单位为像素" 27 > 28 <a-input 29 v-model.number="form.posX" 30 type="number" 31 placeholder="X轴偏移量" 32 addon-after="(X)" 33 /> 34 </a-form-model-item> 35 <a-form-model-item 36 label="二维码定位" 37 prop="posY" 38 extra="Y 代表二维码左顶端的纵坐标,正数,单位为像素" 39 > 40 <a-input 41 v-model.number="form.posY" 42 type="number" 43 placeholder="Y轴偏移量" 44 addon-after="(Y)" 45 /> 46 </a-form-model-item> 47 <a-form-model-item 48 label="二维码尺寸" 49 prop="size" 50 extra="最小280px,最大 1280px" 51 > 52 <a-input 53 v-model.number="form.size" 54 placeholder="二维码尺寸" 55 type="number" 56 /> 57 </a-form-model-item> 58 <a-form-model-item :wrapper-col="{ span: 14, offset: 4 }"> 59 <a-button type="default" @click="cancel"> 60 取消 61 </a-button> 62 <a-button 63 type="primary" 64 @click="onSubmit" 65 style="margin-left:20px" 66 :loading="isLoading" 67 > 68 提交 69 </a-button> 70 </a-form-model-item> 71 </a-form-model> 72 </a-col> 73 <a-col :span="12"> 74 <div> 75 <div style="margin-bottom:10px"> 76 预览尺寸(px):{{ 77 canvasAttr 78 ? canvasAttr.offsetWidth + " x " + canvasAttr.offsetHeight 79 : "" 80 }}(保持纵横比)。 <br />实际尺寸(px):{{ 81 canvasAttr ? canvasAttr.width + " x " + canvasAttr.height : "" 82 }}(与分享卡片尺寸一致)。 83 <!-- <br />{{ 84 qrSize ? "二维码实际尺寸约(px):" + qrSize : "" 85 }}。 --> 86 </div> 87 <canvas ref="canvas" width="750" height="1334"></canvas> 88 </div> 89 </a-col> 90 </a-row> 91 </div> 92 </template> 93 94 <script> 95 import uploadFile from "./UploadFile"; 96 import request from "@/utils/request"; 97 export default { 98 components: { 99 uploadFile 100 }, 101 props: { 102 qrcode: { 103 type: String, 104 default: "" 105 }, 106 form: { 107 type: Object, 108 default: () => { 109 return { 110 id: 0, // 111 img: "", // 分享卡片 112 posX: 0, // 二维码定位X 113 posY: 0, // 二维码定位Y 114 size: 280 //二维码尺寸 115 }; 116 } 117 } 118 }, 119 data() { 120 return { 121 qrSize: null, // 二维码实际尺寸 122 canvasAttr: null, //预览以实际尺寸 123 isLoading: false, 124 id: 0, // 125 canvas: null, // 画布 126 context: null, //context 2d 127 labelCol: { span: 4 }, 128 wrapperCol: { span: 12 }, 129 rules: { 130 img: [ 131 { 132 required: true, 133 message: "请上传分享卡片", 134 trigger: "change" 135 } 136 ], 137 posX: { 138 pattern: /^(0|0\.0*[1-9]+[0-9]*$|[1-9]+[0-9]*\.[0-9]*[0-9]$|[1-9]+[0-9]*$)/, 139 required: true, 140 message: "请输入≥ 0 的坐标", 141 trigger: ["blur", "change"] 142 }, 143 posY: { 144 pattern: /^(0|0\.0*[1-9]+[0-9]*$|[1-9]+[0-9]*\.[0-9]*[0-9]$|[1-9]+[0-9]*$)/, 145 required: true, 146 message: "请输入≥ 0 的坐标", 147 trigger: ["blur", "change"] 148 }, 149 size: [ 150 { 151 type: "number", 152 required: true, 153 message: "最小280px,最大1280px", 154 max: 1280, 155 min: 280, 156 trigger: ["blur", "change"] 157 } 158 ] 159 } 160 }; 161 }, 162 created() { 163 this.$nextTick(() => { 164 this.init(); //初始化 canvas 165 }); 166 }, 167 watch: { 168 form: { 169 // eslint-disable-next-line no-unused-vars 170 handler(newValue, oldValue) { 171 this.drawPoster(); 172 }, 173 deep: true 174 } 175 }, 176 methods: { 177 init() { 178 this.$refs.ruleForm.resetFields(); 179 this.isLoading = false; 180 this.canvas = this.$refs.canvas; 181 this.context = this.canvas.getContext("2d"); 182 this.drawPoster(); 183 }, 184 // 绘制海报 185 drawPoster() { 186 let _this = this; 187 _this.context.clearRect(0, 0, _this.canvas.width, _this.canvas.height); // 清空画布 188 if (!_this.form.img) { 189 return; 190 } 191 let img = new Image(); 192 img.src = this.form.img + "?t=" + new Date().getTime(); // 去掉图片缓存 193 img.setAttribute("crossOrigin", "anonymous"); 194 // console.log(_this.canvas) 195 img.onload = e => { 196 const img = e.path[0]; 197 const img_w = img.width; 198 const img_h = img.height; 199 _this.canvas.width = img_w; 200 _this.canvas.height = img_h; 201 _this.canvasAttr = { 202 offsetWidth: _this.canvas.offsetWidth, 203 offsetHeight: _this.canvas.offsetHeight, 204 width: _this.canvas.width, 205 height: _this.canvas.height 206 }; 207 _this.context.drawImage(img, 0, 0); 208 // 绘制二维码 209 _this.drawQRCode(); 210 }; 211 }, 212 // 绘制二维码 213 drawQRCode() { 214 if (!this.qrcode) { 215 return; 216 } 217 let _this = this; 218 let img = new Image(); 219 img.setAttribute("crossOrigin", "anonymous"); 220 img.src = _this.qrcode + "?t=" + new Date().getTime(); // 去掉图片缓存 221 // console.log(_this.canvas.offsetWidth); 222 // console.log(_this.canvas.width); 223 // console.log(img.width); 224 // const ratio = _this.canvas.offsetWidth / _this.canvas.width; 225 img.onload = e => { 226 const img = e.path[0]; 227 // const ratio = _this.canvas.offsetWidth / img.width; 228 // const size = ((_this.form.size || img.width) * ratio).toFixed(2); 229 // console.log(ratio); 230 // console.log(size); 231 // _this.qrSize = _this.form.size; 232 _this.context.drawImage( 233 img, 234 _this.form.posX, 235 _this.form.posY, 236 _this.form.size, 237 _this.form.size 238 ); 239 }; 240 }, 241 // 提交 242 onSubmit() { 243 // console.log(this.form); 244 this.isLoading = true; 245 this.$refs.ruleForm.validate(async valid => { 246 if (valid) { 247 // console.log(this.form); 248 let base64Url = ""; 249 try { 250 this.$message.loading({ 251 content: "正在保存", 252 key: "savePoster", 253 duration: 5 254 }); 255 base64Url = this.canvas.toDataURL("image/png", 1); 256 const response = ( 257 await request({ 258 url: "/content/file/upload-oss-base64-image", 259 method: "POST", 260 data: { 261 image: base64Url 262 } 263 }) 264 ).data; 265 if (!response.code) return; 266 if (response.code == 200) { 267 this.form.posterImg = response.data; 268 // console.log(this.form); 269 this.$emit("save", this.form); 270 } else { 271 this.$message.error({ 272 content: response.msg || response.message, 273 key: "savePoster", 274 duration: 2 275 }); 276 } 277 this.isLoading = false; 278 } catch (error) { 279 this.$message.error({ 280 content: "上传图片发生错误", 281 key: "savePoster", 282 duration: 2 283 }); 284 this.isLoading = false; 285 // console.log(error); 286 } 287 } else { 288 // console.log("error submit!!"); 289 this.isLoading = false; 290 return false; 291 } 292 }); 293 }, 294 // 上传分享卡片 295 uploadImg(option) { 296 this.form.img = option.imageUrl; 297 }, 298 // 取消上传 299 cancel() { 300 this.$emit("cancel"); 301 } 302 } 303 }; 304 </script> 305 306 <style scoped> 307 canvas { 308 width: 375px; 309 height: auto; 310 background-color: #ffffff; 311 } 312 </style>
作者:胡倩倩0903
本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
分类:
vue
标签:
canvas 绘制海报
posted on 2020-11-24 18:20 kitty20180903suzhou 阅读(626) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY