默认提交
Showing
20 changed files
with
961 additions
and
8 deletions
... | @@ -28,6 +28,7 @@ module.exports = { | ... | @@ -28,6 +28,7 @@ module.exports = { |
28 | */ | 28 | */ |
29 | areaQuery: 'https://api.k.wxpai.cn/bizproxy/kdapi/area', // post 区域查询 | 29 | areaQuery: 'https://api.k.wxpai.cn/bizproxy/kdapi/area', // post 区域查询 |
30 | 30 | ||
31 | uploadFile: '/kdapi/file/upload' //上传图片通用接口 | 31 | wxacodeGet: "/mzcfsapi/qrcode/createV2", // 通用上传 ?path=/pages/index/index?pa=1 |
32 | uploadFile: '/kdapi/file/upload', //上传图片通用接口 | ||
32 | 33 | ||
33 | } | 34 | } | ... | ... |
142 KB
71.3 KB
src/image/blessing/portrait.png
deleted
100644 → 0
52.9 KB
src/image/blessing/top-d1.png
deleted
100644 → 0
406 KB
src/image/poster/poster_c1.png
0 → 100644
537 KB
src/image/poster_c1/poster_c1.png
deleted
100644 → 0
356 KB
src/miniprogram_dist/index/index.js
0 → 100755
1 | const main = { | ||
2 | /** | ||
3 | * 渲染块 | ||
4 | * @param {Object} params | ||
5 | */ | ||
6 | drawBlock({ text, width = 0, height, x, y, paddingLeft = 0, paddingRight = 0, borderWidth, backgroundColor, borderColor, borderRadius = 0, opacity = 1 }) { | ||
7 | // 判断是否块内有文字 | ||
8 | let blockWidth = 0; // 块的宽度 | ||
9 | let textX = 0; | ||
10 | let textY = 0; | ||
11 | if (typeof text !== 'undefined') { | ||
12 | // 如果有文字并且块的宽度小于文字宽度,块的宽度为 文字的宽度 + 内边距 | ||
13 | const textWidth = this._getTextWidth(typeof text.text === 'string' ? text : text.text); | ||
14 | blockWidth = textWidth > width ? textWidth : width; | ||
15 | blockWidth += paddingLeft + paddingLeft; | ||
16 | |||
17 | const { textAlign = 'left', text: textCon } = text; | ||
18 | textY = height / 2 + y; // 文字的y轴坐标在块中线 | ||
19 | if (textAlign === 'left') { | ||
20 | // 如果是右对齐,那x轴在块的最左边 | ||
21 | textX = x + paddingLeft; | ||
22 | } else if (textAlign === 'center') { | ||
23 | textX = blockWidth / 2 + x; | ||
24 | } else { | ||
25 | textX = x + blockWidth - paddingRight; | ||
26 | } | ||
27 | } else { | ||
28 | blockWidth = width; | ||
29 | } | ||
30 | |||
31 | if (backgroundColor) { | ||
32 | // 画面 | ||
33 | this.ctx.save(); | ||
34 | this.ctx.setGlobalAlpha(opacity); | ||
35 | this.ctx.setFillStyle(backgroundColor); | ||
36 | if (borderRadius > 0) { | ||
37 | // 画圆角矩形 | ||
38 | this._drawRadiusRect(x, y, blockWidth, height, borderRadius); | ||
39 | this.ctx.fill(); | ||
40 | } else { | ||
41 | this.ctx.fillRect(this.toPx(x), this.toPx(y), this.toPx(blockWidth), this.toPx(height)); | ||
42 | } | ||
43 | this.ctx.restore(); | ||
44 | } | ||
45 | if (borderWidth) { | ||
46 | // 画线 | ||
47 | this.ctx.save(); | ||
48 | this.ctx.setGlobalAlpha(opacity); | ||
49 | this.ctx.setStrokeStyle(borderColor); | ||
50 | this.ctx.setLineWidth(this.toPx(borderWidth)); | ||
51 | if (borderRadius > 0) { | ||
52 | // 画圆角矩形边框 | ||
53 | this._drawRadiusRect(x, y, blockWidth, height, borderRadius); | ||
54 | this.ctx.stroke(); | ||
55 | } else { | ||
56 | this.ctx.strokeRect(this.toPx(x), this.toPx(y), this.toPx(blockWidth), this.toPx(height)); | ||
57 | } | ||
58 | this.ctx.restore(); | ||
59 | } | ||
60 | |||
61 | if (text) { | ||
62 | this.drawText(Object.assign(text, { x: textX, y: textY })) | ||
63 | } | ||
64 | }, | ||
65 | |||
66 | /** | ||
67 | * 渲染文字 | ||
68 | * @param {Object} params | ||
69 | */ | ||
70 | drawText(params) { | ||
71 | const { x, y, fontSize, color, baseLine, textAlign, text, opacity = 1, width, lineNum, lineHeight } = params; | ||
72 | if (Object.prototype.toString.call(text) === '[object Array]') { | ||
73 | let preText = { x, y, baseLine }; | ||
74 | text.forEach(item => { | ||
75 | preText.x += item.marginLeft || 0; | ||
76 | const textWidth = this._drawSingleText(Object.assign(item, { | ||
77 | ...preText, | ||
78 | })); | ||
79 | preText.x += textWidth + (item.marginRight || 0); // 下一段字的x轴为上一段字x + 上一段字宽度 | ||
80 | }) | ||
81 | } else { | ||
82 | this._drawSingleText(params); | ||
83 | } | ||
84 | }, | ||
85 | |||
86 | /** | ||
87 | * 渲染图片 | ||
88 | */ | ||
89 | drawImage(data) { | ||
90 | const { imgPath, x, y, w, h, sx, sy, sw, sh, borderRadius = 0, borderWidth = 0, borderColor } = data; | ||
91 | this.ctx.save(); | ||
92 | if (borderRadius > 0) { | ||
93 | this._drawRadiusRect(x, y, w, h, borderRadius); | ||
94 | this.ctx.strokeStyle = 'rgba(255,255,255,0)'; | ||
95 | this.ctx.stroke(); | ||
96 | this.ctx.clip(); | ||
97 | this.ctx.drawImage(imgPath, this.toPx(sx), this.toPx(sy), this.toPx(sw), this.toPx(sh), this.toPx(x), this.toPx(y), this.toPx(w), this.toPx(h)); | ||
98 | if (borderWidth > 0) { | ||
99 | this.ctx.setStrokeStyle(borderColor); | ||
100 | this.ctx.setLineWidth(this.toPx(borderWidth)); | ||
101 | this.ctx.stroke(); | ||
102 | } | ||
103 | } else { | ||
104 | this.ctx.drawImage(imgPath, this.toPx(sx), this.toPx(sy), this.toPx(sw), this.toPx(sh), this.toPx(x), this.toPx(y), this.toPx(w), this.toPx(h)); | ||
105 | } | ||
106 | this.ctx.restore(); | ||
107 | }, | ||
108 | /** | ||
109 | * 渲染线 | ||
110 | * @param {*} param0 | ||
111 | */ | ||
112 | drawLine({ startX, startY, endX, endY, color, width }) { | ||
113 | this.ctx.save(); | ||
114 | this.ctx.beginPath(); | ||
115 | this.ctx.setStrokeStyle(color); | ||
116 | this.ctx.setLineWidth(this.toPx(width)); | ||
117 | this.ctx.moveTo(this.toPx(startX), this.toPx(startY)); | ||
118 | this.ctx.lineTo(this.toPx(endX), this.toPx(endY)); | ||
119 | this.ctx.stroke(); | ||
120 | this.ctx.closePath(); | ||
121 | this.ctx.restore(); | ||
122 | }, | ||
123 | downloadResource(images = []) { | ||
124 | const drawList = []; | ||
125 | this.drawArr = []; | ||
126 | images.forEach((image, index) => drawList.push(this._downloadImageAndInfo(image, index))); | ||
127 | return Promise.all(drawList); | ||
128 | }, | ||
129 | initCanvas(w, h, debug) { | ||
130 | return new Promise((resolve) => { | ||
131 | this.setData({ | ||
132 | pxWidth: this.toPx(w), | ||
133 | pxHeight: this.toPx(h), | ||
134 | debug, | ||
135 | }, resolve); | ||
136 | }); | ||
137 | } | ||
138 | } | ||
139 | const handle = { | ||
140 | /** | ||
141 | * 画圆角矩形 | ||
142 | */ | ||
143 | _drawRadiusRect(x, y, w, h, r) { | ||
144 | const br = r / 2; | ||
145 | this.ctx.beginPath(); | ||
146 | this.ctx.moveTo(this.toPx(x + br), this.toPx(y)); // 移动到左上角的点 | ||
147 | this.ctx.lineTo(this.toPx(x + w - br), this.toPx(y)); | ||
148 | this.ctx.arc(this.toPx(x + w - br), this.toPx(y + br), this.toPx(br), 2 * Math.PI * (3 / 4), 2 * Math.PI * (4 / 4)) | ||
149 | this.ctx.lineTo(this.toPx(x + w), this.toPx(y + h - br)); | ||
150 | this.ctx.arc(this.toPx(x + w - br), this.toPx(y + h - br), this.toPx(br), 0, 2 * Math.PI * (1 / 4)) | ||
151 | this.ctx.lineTo(this.toPx(x + br), this.toPx(y + h)); | ||
152 | this.ctx.arc(this.toPx(x + br), this.toPx(y + h - br), this.toPx(br), 2 * Math.PI * (1 / 4), 2 * Math.PI * (2 / 4)) | ||
153 | this.ctx.lineTo(this.toPx(x), this.toPx(y + br)); | ||
154 | this.ctx.arc(this.toPx(x + br), this.toPx(y + br), this.toPx(br), 2 * Math.PI * (2 / 4), 2 * Math.PI * (3 / 4)) | ||
155 | }, | ||
156 | /** | ||
157 | * 计算文本长度 | ||
158 | * @param {Array|Object}} text 数组 或者 对象 | ||
159 | */ | ||
160 | _getTextWidth(text) { | ||
161 | let texts = []; | ||
162 | if (Object.prototype.toString.call(text) === '[object Object]') { | ||
163 | texts.push(text); | ||
164 | } else { | ||
165 | texts = text; | ||
166 | } | ||
167 | let width = 0; | ||
168 | texts.forEach(({ fontSize, text, marginLeft = 0, marginRight = 0 }) => { | ||
169 | this.ctx.setFontSize(this.toPx(fontSize)); | ||
170 | width += this.ctx.measureText(text).width + marginLeft + marginRight; | ||
171 | }) | ||
172 | |||
173 | return this.toRpx(width); | ||
174 | }, | ||
175 | /** | ||
176 | * 渲染一段文字 | ||
177 | */ | ||
178 | _drawSingleText({ x, y, fontSize, color, baseLine, textAlign = 'left', text, opacity = 1, textDecoration = 'none', | ||
179 | width, lineNum = 1, lineHeight = 0, fontWeight = 'normal', fontStyle = 'normal', fontFamily = "sans-serif"}) { | ||
180 | this.ctx.save(); | ||
181 | this.ctx.beginPath(); | ||
182 | this.ctx.font = fontStyle + " " + fontWeight + " " + this.toPx(fontSize, true) + "px " + fontFamily | ||
183 | this.ctx.setGlobalAlpha(opacity); | ||
184 | // this.ctx.setFontSize(this.toPx(fontSize)); | ||
185 | this.ctx.setFillStyle(color); | ||
186 | this.ctx.setTextBaseline(baseLine); | ||
187 | this.ctx.setTextAlign(textAlign); | ||
188 | let textWidth = this.toRpx(this.ctx.measureText(text).width); | ||
189 | const textArr = []; | ||
190 | if (textWidth > width) { | ||
191 | // 文本宽度 大于 渲染宽度 | ||
192 | let fillText = ''; | ||
193 | let line = 1; | ||
194 | for (let i = 0; i <= text.length - 1 ; i++) { // 将文字转为数组,一行文字一个元素 | ||
195 | fillText = fillText + text[i]; | ||
196 | if (this.toRpx(this.ctx.measureText(fillText).width) >= width) { | ||
197 | if (line === lineNum) { | ||
198 | if (i !== text.length - 1) { | ||
199 | fillText = fillText.substring(0, fillText.length - 1) + '...'; | ||
200 | } | ||
201 | } | ||
202 | if(line <= lineNum) { | ||
203 | textArr.push(fillText); | ||
204 | } | ||
205 | fillText = ''; | ||
206 | line++; | ||
207 | } else { | ||
208 | if(line <= lineNum) { | ||
209 | if(i === text.length -1){ | ||
210 | textArr.push(fillText); | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | textWidth = width; | ||
216 | } else { | ||
217 | textArr.push(text); | ||
218 | } | ||
219 | |||
220 | textArr.forEach((item, index) => { | ||
221 | this.ctx.fillText(item, this.toPx(x), this.toPx(y + (lineHeight || fontSize) * index)); | ||
222 | }) | ||
223 | |||
224 | this.ctx.restore(); | ||
225 | |||
226 | // textDecoration | ||
227 | if (textDecoration !== 'none') { | ||
228 | let lineY = y; | ||
229 | if (textDecoration === 'line-through') { | ||
230 | // 目前只支持贯穿线 | ||
231 | lineY = y; | ||
232 | } | ||
233 | this.ctx.save(); | ||
234 | this.ctx.moveTo(this.toPx(x), this.toPx(lineY)); | ||
235 | this.ctx.lineTo(this.toPx(x) + this.toPx(textWidth), this.toPx(lineY)); | ||
236 | this.ctx.setStrokeStyle(color); | ||
237 | this.ctx.stroke(); | ||
238 | this.ctx.restore(); | ||
239 | } | ||
240 | |||
241 | return textWidth; | ||
242 | }, | ||
243 | } | ||
244 | const helper = { | ||
245 | /** | ||
246 | * 下载图片并获取图片信息 | ||
247 | */ | ||
248 | _downloadImageAndInfo(image, index) { | ||
249 | return new Promise((resolve, reject) => { | ||
250 | const { x, y, url, zIndex } = image; | ||
251 | const imageUrl = url; | ||
252 | // 下载图片 | ||
253 | this._downImage(imageUrl, index) | ||
254 | // 获取图片信息 | ||
255 | .then(imgPath => this._getImageInfo(imgPath, index)) | ||
256 | .then(({ imgPath, imgInfo }) => { | ||
257 | // 根据画布的宽高计算出图片绘制的大小,这里会保证图片绘制不变形 | ||
258 | let sx; | ||
259 | let sy; | ||
260 | const borderRadius = image.borderRadius || 0; | ||
261 | const setWidth = image.width; | ||
262 | const setHeight = image.height; | ||
263 | const width = this.toRpx(imgInfo.width); | ||
264 | const height = this.toRpx(imgInfo.height); | ||
265 | |||
266 | if (width / height <= setWidth / setHeight) { | ||
267 | sx = 0; | ||
268 | sy = (height - ((width / setWidth) * setHeight)) / 2; | ||
269 | } else { | ||
270 | sy = 0; | ||
271 | sx = (width - ((height / setHeight) * setWidth)) / 2; | ||
272 | } | ||
273 | this.drawArr.push({ | ||
274 | type: 'image', | ||
275 | borderRadius, | ||
276 | borderWidth: image.borderWidth, | ||
277 | borderColor: image.borderColor, | ||
278 | zIndex: typeof zIndex !== 'undefined' ? zIndex : index, | ||
279 | imgPath, | ||
280 | sx, | ||
281 | sy, | ||
282 | sw: (width - (sx * 2)), | ||
283 | sh: (height - (sy * 2)), | ||
284 | x, | ||
285 | y, | ||
286 | w: setWidth, | ||
287 | h: setHeight, | ||
288 | }); | ||
289 | resolve(); | ||
290 | }) | ||
291 | .catch(err => reject(err)); | ||
292 | }); | ||
293 | }, | ||
294 | /** | ||
295 | * 下载图片资源 | ||
296 | * @param {*} imageUrl | ||
297 | */ | ||
298 | _downImage(imageUrl) { | ||
299 | return new Promise((resolve, reject) => { | ||
300 | if (/^http/.test(imageUrl) && !new RegExp(wx.env.USER_DATA_PATH).test(imageUrl)) { | ||
301 | wx.downloadFile({ | ||
302 | url: this._mapHttpToHttps(imageUrl), | ||
303 | success: (res) => { | ||
304 | if (res.statusCode === 200) { | ||
305 | resolve(res.tempFilePath); | ||
306 | } else { | ||
307 | reject(res.errMsg); | ||
308 | } | ||
309 | }, | ||
310 | fail(err) { | ||
311 | reject(err); | ||
312 | }, | ||
313 | }); | ||
314 | } else { | ||
315 | // 支持本地地址 | ||
316 | resolve(imageUrl); | ||
317 | } | ||
318 | }); | ||
319 | }, | ||
320 | /** | ||
321 | * 获取图片信息 | ||
322 | * @param {*} imgPath | ||
323 | * @param {*} index | ||
324 | */ | ||
325 | _getImageInfo(imgPath, index) { | ||
326 | return new Promise((resolve, reject) => { | ||
327 | wx.getImageInfo({ | ||
328 | src: imgPath, | ||
329 | success(res) { | ||
330 | resolve({ imgPath, imgInfo: res, index }); | ||
331 | }, | ||
332 | fail(err) { | ||
333 | reject(err); | ||
334 | }, | ||
335 | }); | ||
336 | }); | ||
337 | }, | ||
338 | toPx(rpx, int) { | ||
339 | if (int) { | ||
340 | return parseInt(rpx * this.factor); | ||
341 | } | ||
342 | return rpx * this.factor; | ||
343 | }, | ||
344 | toRpx(px, int) { | ||
345 | if (int) { | ||
346 | return parseInt(px / this.factor); | ||
347 | } | ||
348 | return px / this.factor; | ||
349 | }, | ||
350 | /** | ||
351 | * 将http转为https | ||
352 | * @param {String}} rawUrl 图片资源url | ||
353 | */ | ||
354 | _mapHttpToHttps(rawUrl) { | ||
355 | if (rawUrl.indexOf(':') < 0) { | ||
356 | return rawUrl; | ||
357 | } | ||
358 | const urlComponent = rawUrl.split(':'); | ||
359 | if (urlComponent.length === 2) { | ||
360 | if (urlComponent[0] === 'http') { | ||
361 | urlComponent[0] = 'https'; | ||
362 | return `${urlComponent[0]}:${urlComponent[1]}`; | ||
363 | } | ||
364 | } | ||
365 | return rawUrl; | ||
366 | }, | ||
367 | } | ||
368 | Component({ | ||
369 | properties: { | ||
370 | }, | ||
371 | created() { | ||
372 | const sysInfo = wx.getSystemInfoSync(); | ||
373 | const screenWidth = sysInfo.screenWidth; | ||
374 | this.factor = screenWidth / 750; | ||
375 | }, | ||
376 | methods: Object.assign({ | ||
377 | /** | ||
378 | * 计算画布的高度 | ||
379 | * @param {*} config | ||
380 | */ | ||
381 | getHeight(config) { | ||
382 | const getTextHeight = (text) => { | ||
383 | let fontHeight = text.lineHeight || text.fontSize; | ||
384 | let height = 0; | ||
385 | if (text.baseLine === 'top') { | ||
386 | height = fontHeight; | ||
387 | } else if (text.baseLine === 'middle') { | ||
388 | height = fontHeight / 2; | ||
389 | } else { | ||
390 | height = 0; | ||
391 | } | ||
392 | return height; | ||
393 | } | ||
394 | const heightArr = []; | ||
395 | (config.blocks || []).forEach((item) => { | ||
396 | heightArr.push(item.y + item.height); | ||
397 | }); | ||
398 | (config.texts || []).forEach((item) => { | ||
399 | let height; | ||
400 | if (Object.prototype.toString.call(item.text) === '[object Array]') { | ||
401 | item.text.forEach((i) => { | ||
402 | height = getTextHeight({...i, baseLine: item.baseLine}); | ||
403 | heightArr.push(item.y + height); | ||
404 | }); | ||
405 | } else { | ||
406 | height = getTextHeight(item); | ||
407 | heightArr.push(item.y + height); | ||
408 | } | ||
409 | }); | ||
410 | (config.images || []).forEach((item) => { | ||
411 | heightArr.push(item.y + item.height); | ||
412 | }); | ||
413 | (config.lines || []).forEach((item) => { | ||
414 | heightArr.push(item.startY); | ||
415 | heightArr.push(item.endY); | ||
416 | }); | ||
417 | const sortRes = heightArr.sort((a, b) => b - a); | ||
418 | let canvasHeight = 0; | ||
419 | if (sortRes.length > 0) { | ||
420 | canvasHeight = sortRes[0]; | ||
421 | } | ||
422 | if (config.height < canvasHeight || !config.height) { | ||
423 | return canvasHeight; | ||
424 | } else { | ||
425 | return config.height; | ||
426 | } | ||
427 | }, | ||
428 | create(config) { | ||
429 | this.ctx = wx.createCanvasContext('canvasid', this); | ||
430 | |||
431 | const height = this.getHeight(config); | ||
432 | this.initCanvas(config.width, height, config.debug) | ||
433 | .then(() => { | ||
434 | // 设置画布底色 | ||
435 | if (config.backgroundColor) { | ||
436 | this.ctx.save(); | ||
437 | this.ctx.setFillStyle(config.backgroundColor); | ||
438 | this.ctx.fillRect(0, 0, this.toPx(config.width), this.toPx(height)); | ||
439 | this.ctx.restore(); | ||
440 | } | ||
441 | const { texts = [], images = [], blocks = [], lines = [] } = config; | ||
442 | const queue = this.drawArr | ||
443 | .concat(texts.map((item) => { | ||
444 | item.type = 'text'; | ||
445 | item.zIndex = item.zIndex || 0; | ||
446 | return item; | ||
447 | })) | ||
448 | .concat(blocks.map((item) => { | ||
449 | item.type = 'block'; | ||
450 | item.zIndex = item.zIndex || 0; | ||
451 | return item; | ||
452 | })) | ||
453 | .concat(lines.map((item) => { | ||
454 | item.type = 'line'; | ||
455 | item.zIndex = item.zIndex || 0; | ||
456 | return item; | ||
457 | })); | ||
458 | // 按照顺序排序 | ||
459 | queue.sort((a, b) => a.zIndex - b.zIndex); | ||
460 | |||
461 | queue.forEach((item) => { | ||
462 | if (item.type === 'image') { | ||
463 | this.drawImage(item) | ||
464 | } else if (item.type === 'text') { | ||
465 | this.drawText(item) | ||
466 | } else if (item.type === 'block') { | ||
467 | this.drawBlock(item) | ||
468 | } else if (item.type === 'line') { | ||
469 | this.drawLine(item) | ||
470 | } | ||
471 | }); | ||
472 | |||
473 | const res = wx.getSystemInfoSync(); | ||
474 | const platform = res.platform; | ||
475 | let time = 0; | ||
476 | if (platform === 'android') { | ||
477 | // 在安卓平台,经测试发现如果海报过于复杂在转换时需要做延时,要不然样式会错乱 | ||
478 | time = 300; | ||
479 | } | ||
480 | this.ctx.draw(false, () => { | ||
481 | setTimeout(() => { | ||
482 | wx.canvasToTempFilePath({ | ||
483 | canvasId: 'canvasid', | ||
484 | success: (res) => { | ||
485 | this.triggerEvent('success', res.tempFilePath); | ||
486 | }, | ||
487 | fail: (err) => { | ||
488 | this.triggerEvent('fail', err); | ||
489 | }, | ||
490 | }, this); | ||
491 | }, time); | ||
492 | }); | ||
493 | }) | ||
494 | .catch((err) => { | ||
495 | wx.showToast({ icon: 'none', title: err.errMsg || '生成失败' }); | ||
496 | console.error(err); | ||
497 | }); | ||
498 | }, | ||
499 | }, main, handle, helper), | ||
500 | }); | ||
501 |
src/miniprogram_dist/index/index.json
0 → 100755
src/miniprogram_dist/index/index.wxml
0 → 100755
src/miniprogram_dist/index/index.wxss
0 → 100755
1 | .canvas { | ||
2 | width: 750rpx; | ||
3 | height: 750rpx; | ||
4 | } | ||
5 | .canvas.pro { | ||
6 | position: absolute; | ||
7 | bottom: 0; | ||
8 | left: 0; | ||
9 | transform: translate3d(-9999rpx, 0, 0); | ||
10 | } | ||
11 | .canvas.debug { | ||
12 | position: absolute; | ||
13 | bottom: 0; | ||
14 | left: 0; | ||
15 | border: 1rpx solid #ccc; | ||
16 | } | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/miniprogram_dist/poster/index.js
0 → 100755
1 | Component({ | ||
2 | properties: { | ||
3 | config: { | ||
4 | type: Object, | ||
5 | value: {}, | ||
6 | }, | ||
7 | preload: { // 是否预下载图片资源 | ||
8 | type: Boolean, | ||
9 | value: false, | ||
10 | }, | ||
11 | hideLoading: { // 是否隐藏loading | ||
12 | type: Boolean, | ||
13 | value: false, | ||
14 | } | ||
15 | }, | ||
16 | ready() { | ||
17 | if (this.data.preload) { | ||
18 | const poster = this.selectComponent('#poster'); | ||
19 | this.downloadStatus = 'doing'; | ||
20 | poster.downloadResource(this.data.config.images).then(() => { | ||
21 | this.downloadStatus = 'success'; | ||
22 | this.trigger('downloadSuccess'); | ||
23 | }).catch((e) => { | ||
24 | this.downloadStatus = 'fail'; | ||
25 | this.trigger('downloadFail', e); | ||
26 | }); | ||
27 | } | ||
28 | }, | ||
29 | methods: { | ||
30 | trigger(event, data) { | ||
31 | if (this.listener && typeof this.listener[event] === 'function') { | ||
32 | this.listener[event](data); | ||
33 | } | ||
34 | }, | ||
35 | once(event, fun) { | ||
36 | if (typeof this.listener === 'undefined') { | ||
37 | this.listener = {}; | ||
38 | } | ||
39 | this.listener[event] = fun; | ||
40 | }, | ||
41 | downloadResource(reset) { | ||
42 | return new Promise((resolve, reject) => { | ||
43 | if (reset) { | ||
44 | this.downloadStatus = null; | ||
45 | } | ||
46 | const poster = this.selectComponent('#poster'); | ||
47 | if (this.downloadStatus && this.downloadStatus !== 'fail') { | ||
48 | if (this.downloadStatus === 'success') { | ||
49 | resolve(); | ||
50 | } else { | ||
51 | this.once('downloadSuccess', () => resolve()); | ||
52 | this.once('downloadFail', (e) => reject(e)); | ||
53 | } | ||
54 | } else { | ||
55 | poster.downloadResource(this.data.config.images) | ||
56 | .then(() => { | ||
57 | this.downloadStatus = 'success'; | ||
58 | resolve(); | ||
59 | }) | ||
60 | .catch((e) => reject(e)); | ||
61 | } | ||
62 | }) | ||
63 | }, | ||
64 | onCreate(reset = false) { | ||
65 | !this.data.hideLoading && wx.showLoading({ mask: true, title: '生成中' }); | ||
66 | return this.downloadResource(typeof reset === 'boolean' && reset).then(() => { | ||
67 | !this.data.hideLoading && wx.hideLoading(); | ||
68 | const poster = this.selectComponent('#poster'); | ||
69 | poster.create(this.data.config); | ||
70 | }) | ||
71 | .catch((err) => { | ||
72 | !this.data.hideLoading && wx.hideLoading(); | ||
73 | wx.showToast({ icon: 'none', title: err.errMsg || '生成失败' }); | ||
74 | console.error(err); | ||
75 | this.triggerEvent('fail', err); | ||
76 | }) | ||
77 | }, | ||
78 | onCreateSuccess(e) { | ||
79 | const { detail } = e; | ||
80 | this.triggerEvent('success', detail); | ||
81 | }, | ||
82 | onCreateFail(err) { | ||
83 | console.error(err); | ||
84 | this.triggerEvent('fail', err); | ||
85 | } | ||
86 | } | ||
87 | }) | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
src/miniprogram_dist/poster/index.json
0 → 100755
src/miniprogram_dist/poster/index.wxml
0 → 100755
src/miniprogram_dist/poster/index.wxss
0 → 100755
File mode changed
src/miniprogram_dist/poster/poster.js
0 → 100755
1 | const defaultOptions = { | ||
2 | selector: '#poster' | ||
3 | }; | ||
4 | |||
5 | function Poster(options = {}) { | ||
6 | options = { | ||
7 | ...defaultOptions, | ||
8 | ...options, | ||
9 | }; | ||
10 | |||
11 | const pages = getCurrentPages(); | ||
12 | const ctx = pages[pages.length - 1]; | ||
13 | |||
14 | const poster = ctx.selectComponent(options.selector); | ||
15 | delete options.selector; | ||
16 | |||
17 | return poster; | ||
18 | }; | ||
19 | |||
20 | Poster.create = (reset = false) => { | ||
21 | const poster = Poster(); | ||
22 | if (!poster) { | ||
23 | console.error('请设置组件的id="poster"!!!'); | ||
24 | } else { | ||
25 | return Poster().onCreate(reset); | ||
26 | } | ||
27 | } | ||
28 | |||
29 | export default Poster; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -4,6 +4,7 @@ import { | ... | @@ -4,6 +4,7 @@ import { |
4 | } from '../../utils/util'; | 4 | } from '../../utils/util'; |
5 | 5 | ||
6 | import Dialog from '../../ui/vant-weapp/dialog/dialog'; | 6 | import Dialog from '../../ui/vant-weapp/dialog/dialog'; |
7 | import Poster from '../../miniprogram_dist/poster/poster'; | ||
7 | 8 | ||
8 | const back = wx.getBackgroundAudioManager(); | 9 | const back = wx.getBackgroundAudioManager(); |
9 | 10 | ||
... | @@ -41,7 +42,9 @@ Page({ | ... | @@ -41,7 +42,9 @@ Page({ |
41 | isPlayingBgm: false, | 42 | isPlayingBgm: false, |
42 | isMore: false, | 43 | isMore: false, |
43 | 44 | ||
44 | // shortcutPics:["red-package2","more-bless"] | 45 | imageUrl: "", // 海报图 |
46 | posterVisible: false, | ||
47 | wxCodeUrl: "", // 微信小程序码 | ||
45 | }, | 48 | }, |
46 | onShareAppMessage(res) { | 49 | onShareAppMessage(res) { |
47 | let shareType = "" | 50 | let shareType = "" |
... | @@ -128,7 +131,6 @@ Page({ | ... | @@ -128,7 +131,6 @@ Page({ |
128 | */ | 131 | */ |
129 | initData() {}, | 132 | initData() {}, |
130 | playBgm() { | 133 | playBgm() { |
131 | return; | ||
132 | let _this = this; | 134 | let _this = this; |
133 | let { | 135 | let { |
134 | detailData | 136 | detailData |
... | @@ -192,11 +194,273 @@ Page({ | ... | @@ -192,11 +194,273 @@ Page({ |
192 | } | 194 | } |
193 | }, | 195 | }, |
194 | 196 | ||
197 | onHidePosterHandler() { | ||
198 | this.setData({ | ||
199 | posterVisible: false | ||
200 | }) | ||
201 | }, | ||
202 | |||
203 | /** | ||
204 | * 图片查看 | ||
205 | */ | ||
206 | onPreviewImageHandler(evt) { | ||
207 | let current = this.data.imageUrl; | ||
208 | let urls = [current]; | ||
209 | wx.previewImage({ | ||
210 | current: current, | ||
211 | urls: urls | ||
212 | }) | ||
213 | }, | ||
214 | |||
215 | /** | ||
216 | * 保存图片到本地 | ||
217 | */ | ||
218 | saveImageToPhotosAlbum() { | ||
219 | let _this = this; | ||
220 | wx.saveImageToPhotosAlbum({ | ||
221 | filePath: _this.data.imageUrl, | ||
222 | success(res) { | ||
223 | wx.showToast({ | ||
224 | title: '保存成功', | ||
225 | icon: 'success' | ||
226 | }); | ||
227 | }, | ||
228 | fail(err) { | ||
229 | wx.getSetting({ | ||
230 | success: (res) => { | ||
231 | if (!res.authSetting['scope.writePhotosAlbum']) { | ||
232 | // 未授权 | ||
233 | wx.showModal({ | ||
234 | title: '提示', | ||
235 | content: '小程序请求访问相册权限', | ||
236 | confirmText: '前往授权', | ||
237 | success(res) { | ||
238 | if (res.confirm) { | ||
239 | wx.openSetting({ | ||
240 | success(res) {} | ||
241 | }) | ||
242 | } else if (res.cancel) {} | ||
243 | } | ||
244 | }) | ||
245 | } | ||
246 | } | ||
247 | }) | ||
248 | } | ||
249 | }) | ||
250 | }, | ||
251 | |||
252 | // 配置小程序码 | ||
253 | getWxCode() { | ||
254 | let { | ||
255 | detailData | ||
256 | } = this.data; | ||
257 | let memberCode = app.store.getItem("memberCode"); | ||
258 | let wxCodePath = `pages/blessing/blessing?c=${detailData.blessCode}&m=${memberCode}`; | ||
259 | |||
260 | return new Promise((resolve, reject) => { | ||
261 | // 先获取小程序码 | ||
262 | app.get({ | ||
263 | mode: "common", | ||
264 | url: app.api.wxacodeGet, | ||
265 | data: { | ||
266 | path: encodeURIComponent(wxCodePath) | ||
267 | } | ||
268 | }).then((result) => { | ||
269 | this.setData({ | ||
270 | wxCodeUrl: result | ||
271 | }) | ||
272 | resolve(result); | ||
273 | }) | ||
274 | }); | ||
275 | |||
276 | }, | ||
277 | |||
195 | /** | 278 | /** |
196 | * 分享图片祝福 | 279 | * 分享图片祝福 |
197 | * 生成海报 | 280 | * 生成海报 |
198 | */ | 281 | */ |
199 | onPosterHandler(){ | 282 | onPosterHandler() { |
283 | console.log("onPosterHandler"); | ||
284 | |||
285 | // 先获取小程序码 | ||
286 | this.getWxCode().then((result) => { | ||
287 | let posterData = this.getPosterConfig(); | ||
288 | this.onCreatePoster(posterData); | ||
289 | }); | ||
290 | }, | ||
291 | |||
292 | onPosterSuccess(e) { | ||
293 | wx.hideLoading(); | ||
294 | const { | ||
295 | detail | ||
296 | } = e; | ||
297 | console.log("detail:", detail) | ||
298 | this.setData({ | ||
299 | imageUrl: detail, | ||
300 | posterVisible: true, | ||
301 | }) | ||
302 | }, | ||
303 | |||
304 | onPosterFail(err) { | ||
305 | wx.hideLoading(); | ||
306 | console.error(err); | ||
307 | }, | ||
308 | |||
309 | /** | ||
310 | * 异步生成海报 | ||
311 | */ | ||
312 | onCreatePoster(posterConfig) { | ||
313 | console.log("posterConfig:", posterConfig); | ||
314 | this.setData({ | ||
315 | posterConfig: posterConfig | ||
316 | }, () => { | ||
317 | Poster.create(true); // 入参:true为抹掉重新生成 | ||
318 | }); | ||
319 | }, | ||
320 | |||
321 | // 获取海报数据 | ||
322 | getPosterConfig() { | ||
323 | let { | ||
324 | detailData, | ||
325 | ownerMember, | ||
326 | memberList, | ||
327 | wxCodeUrl | ||
328 | } = this.data; | ||
329 | let avatarWid = 120; | ||
330 | let nickname = ownerMember.memberName; | ||
331 | let avatar = ownerMember.memberHead; | ||
332 | let desc = ""; | ||
333 | if (detailData.type == 1) { | ||
334 | // 组队 | ||
335 | if (detailData.count > 0) { | ||
336 | desc = `携${detailData.familyName}${detailData.count}人` | ||
337 | } else { | ||
338 | desc = `携${detailData.familyName}` | ||
339 | } | ||
340 | } else { | ||
341 | // 单人 | ||
342 | desc = `向各位亲朋好友` | ||
343 | } | ||
344 | let blocks = [] | ||
345 | let images = [ | ||
346 | // 背景图 | ||
347 | { | ||
348 | x: 0, | ||
349 | y: 0, | ||
350 | width: 750, | ||
351 | height: 1334, | ||
352 | url: "../../image/poster/poster_c1.png", | ||
353 | }, | ||
354 | // 头像 | ||
355 | { | ||
356 | x: 184, | ||
357 | y: 186, | ||
358 | width: avatarWid, | ||
359 | height: avatarWid, | ||
360 | borderRadius: avatarWid, | ||
361 | zIndex: 22, | ||
362 | url: avatar, | ||
363 | }, | ||
364 | // 小程序码 | ||
365 | { | ||
366 | width: 100, | ||
367 | height: 100, | ||
368 | x: 586, | ||
369 | y: 1198, | ||
370 | url: wxCodeUrl, | ||
371 | zIndex: 31 | ||
372 | } | ||
373 | ]; | ||
374 | let lines = []; | ||
375 | let texts = [ | ||
376 | // 房主昵称 | ||
377 | { | ||
378 | x: 53, | ||
379 | y: 460, | ||
380 | fontSize: 60, | ||
381 | color: "#fee085", | ||
382 | textAlign: "left", | ||
383 | zIndex: 11, | ||
384 | text: nickname, | ||
385 | }, | ||
386 | // 文案 | ||
387 | { | ||
388 | x: 53, | ||
389 | y: 540, | ||
390 | fontSize: 60, | ||
391 | color: "#fee085", | ||
392 | textAlign: "left", | ||
393 | zIndex: 11, | ||
394 | text: desc, | ||
395 | } | ||
396 | ]; | ||
397 | |||
398 | // 组队补充 | ||
399 | if (detailData.type == 1) { | ||
400 | // 头像昵称取出3人 | ||
401 | let member1 = memberList[0] || null; | ||
402 | let member2 = memberList[1] || null; | ||
403 | let member3 = memberList[2] || null; | ||
404 | |||
405 | let memberDrawList = []; | ||
406 | if (member1) memberDrawList.push(member1); | ||
407 | if (member2) memberDrawList.push(member2); | ||
408 | if (member3) memberDrawList.push(member3); | ||
409 | let endX = 630; | ||
410 | let wid = 160; | ||
411 | let startY = 800; | ||
412 | memberDrawList.forEach((element, idx) => { | ||
413 | // blocks.push({ | ||
414 | // x: endX - (idx * wid) - ((wid - 92)), | ||
415 | // y: startY, | ||
416 | // width: 92, | ||
417 | // height: 92, | ||
418 | // borderRadius: 92, | ||
419 | // zIndex: 90, | ||
420 | // borderColor: "#fee085", | ||
421 | // }) | ||
422 | images.push({ | ||
423 | x: endX - (idx * wid) - ((wid - 92) / 2) - 4, | ||
424 | y: startY + 2, | ||
425 | width: 92, | ||
426 | height: 92, | ||
427 | borderRadius: 92, | ||
428 | zIndex: 101, | ||
429 | url: avatar, | ||
430 | }) | ||
431 | texts.push({ | ||
432 | x: endX - (idx * wid), | ||
433 | y: startY + 136, | ||
434 | width: wid, | ||
435 | fontSize: 24, | ||
436 | color: "#fee085", | ||
437 | textAlign: "center", | ||
438 | zIndex: 101, | ||
439 | text: element.memberName, | ||
440 | }) | ||
441 | }); | ||
442 | |||
443 | texts.push({ | ||
444 | x: 686, | ||
445 | y: startY + 200, | ||
446 | fontSize: 48, | ||
447 | color: "#fee085", | ||
448 | textAlign: "right", | ||
449 | zIndex: 11, | ||
450 | text: `长按查看全部${detailData.count}人 >>`, | ||
451 | }) | ||
452 | } | ||
453 | |||
454 | let posterData = { | ||
455 | width: 750, | ||
456 | height: 1334, | ||
457 | debug: false, | ||
458 | blocks: blocks, | ||
459 | images: images, | ||
460 | lines: lines, | ||
461 | texts: texts, | ||
462 | } | ||
463 | return posterData; | ||
200 | }, | 464 | }, |
201 | 465 | ||
202 | // 显示更新用户信息 | 466 | // 显示更新用户信息 | ... | ... |
... | @@ -5,6 +5,10 @@ | ... | @@ -5,6 +5,10 @@ |
5 | font-size: 36rpx; | 5 | font-size: 36rpx; |
6 | } | 6 | } |
7 | 7 | ||
8 | .van-popup { | ||
9 | background-color: transparent !important; | ||
10 | } | ||
11 | |||
8 | // 用户头像 | 12 | // 用户头像 |
9 | .portrait { | 13 | .portrait { |
10 | position: relative; | 14 | position: relative; |
... | @@ -65,6 +69,24 @@ | ... | @@ -65,6 +69,24 @@ |
65 | text-align: center; | 69 | text-align: center; |
66 | } | 70 | } |
67 | 71 | ||
72 | .poster { | ||
73 | width: 540px; | ||
74 | } | ||
75 | |||
76 | .save-btn { | ||
77 | margin: 24px auto; | ||
78 | @include btc(290px, 76px); | ||
79 | color: #fee085; | ||
80 | font-size: 36px; | ||
81 | border-radius: 8px; | ||
82 | box-shadow: 5.9px 5.5px 18px 0 rgba(26, 36, 91, 0.73); | ||
83 | background-image: linear-gradient(to top, #b41d36, #bb2634); | ||
84 | |||
85 | color: #940023; | ||
86 | box-shadow: 5.9px 5.5px 18px 0 rgba(26, 36, 91, 0.73); | ||
87 | background-image: linear-gradient(to top, #f4b44d, #e8b976, #ffebb5); | ||
88 | } | ||
89 | |||
68 | .page { | 90 | .page { |
69 | padding-bottom: 200px; | 91 | padding-bottom: 200px; |
70 | 92 | ||
... | @@ -321,7 +343,7 @@ | ... | @@ -321,7 +343,7 @@ |
321 | position: fixed; | 343 | position: fixed; |
322 | bottom: 0; | 344 | bottom: 0; |
323 | width: 100%; | 345 | width: 100%; |
324 | z-index: 101; | 346 | z-index: 21; |
325 | 347 | ||
326 | .bottom-bg { | 348 | .bottom-bg { |
327 | width: 750px; | 349 | width: 750px; |
... | @@ -362,7 +384,7 @@ | ... | @@ -362,7 +384,7 @@ |
362 | position: fixed; | 384 | position: fixed; |
363 | right: 24px; | 385 | right: 24px; |
364 | top: 24px; | 386 | top: 24px; |
365 | z-index: 201; | 387 | z-index: 31; |
366 | } | 388 | } |
367 | 389 | ||
368 | 390 | ||
... | @@ -403,3 +425,7 @@ | ... | @@ -403,3 +425,7 @@ |
403 | -webkit-transform: rotate(360deg); | 425 | -webkit-transform: rotate(360deg); |
404 | } | 426 | } |
405 | } | 427 | } |
428 | |||
429 | #poster { | ||
430 | position: absolute; | ||
431 | } | ... | ... |
1 | <poster id="poster" hide-loading="{{true}}" preload="{{false}}" config="{{posterConfig}}" bind:success="onPosterSuccess" bind:fail="onPosterFail"></poster> | ||
1 | <view class="page"> | 2 | <view class="page"> |
2 | <view class="app__bgc bgc" style="background-color: {{detailData.background}};"></view> | 3 | <view class="app__bgc bgc" style="background-color: {{detailData.background}};"></view> |
3 | <!-- <view class="app__bg bg " style="background: url('{{detailData.backgroundImage}}')"></view> --> | 4 | <!-- <view class="app__bg bg " style="background: url('{{detailData.backgroundImage}}')"></view> --> |
... | @@ -67,7 +68,7 @@ | ... | @@ -67,7 +68,7 @@ |
67 | <!-- 尾部头像 --> | 68 | <!-- 尾部头像 --> |
68 | <view class="portrait"> | 69 | <view class="portrait"> |
69 | <image class="portrait-inner" mode="scaleToFill" src="{{ownerMember.memberHead}}" /> | 70 | <image class="portrait-inner" mode="scaleToFill" src="{{ownerMember.memberHead}}" /> |
70 | <image class="portrait-border" mode="scaleToFill" src="{{detailData.headFrame}}" /> | 71 | <image class="portrait-border ani-rotation" mode="scaleToFill" src="{{detailData.headFrame}}" /> |
71 | </view> | 72 | </view> |
72 | <view class="name"> | 73 | <view class="name"> |
73 | <view class="tt t1">{{ownerMember.memberName}}</view> | 74 | <view class="tt t1">{{ownerMember.memberName}}</view> |
... | @@ -185,4 +186,11 @@ | ... | @@ -185,4 +186,11 @@ |
185 | <view class="t1">邀请你一起加入组队送祝福!</view> | 186 | <view class="t1">邀请你一起加入组队送祝福!</view> |
186 | </view> | 187 | </view> |
187 | </van-dialog> | 188 | </van-dialog> |
189 | <van-popup show="{{ authorizeVisible }}"> | ||
190 | <authorize-comp bind:evtcomp="evtcomp"></authorize-comp> | ||
191 | </van-popup> | ||
192 | <van-popup bind:click-overlay="onHidePosterHandler" show="{{ posterVisible }}"> | ||
193 | <image bindtap="onPreviewImageHandler" class="poster" mode="widthFix" src="{{imageUrl}}" /> | ||
194 | <view bindtap="saveImageToPhotosAlbum" class="save-btn">一键保存</view> | ||
195 | </van-popup> | ||
188 | <shortcut2 types="{{[]}}" pics="{{['red-package2','more-bless']}}"></shortcut2> | 196 | <shortcut2 types="{{[]}}" pics="{{['red-package2','more-bless']}}"></shortcut2> | ... | ... |
-
Please register or sign in to post a comment