11d4ac48 by simon

no message

1 parent cbf060d5
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
1 {
2 "component": true
3 }
...\ No newline at end of file ...\ No newline at end of file
1 <!--index.wxml-->
2 <view class="container">
3 <canvas canvas-id='canvasid' class="canvas {{debug ? 'debug' : 'pro'}}" style='width: {{pxWidth}}px; height: {{pxHeight}}px;'></canvas>
4 </view>
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
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
1 {
2 "component": true,
3 "usingComponents": {
4 "we-canvas": "../index/index"
5 }
6 }
...\ No newline at end of file ...\ No newline at end of file
1 <view bindtap='onCreate'>
2 <slot/>
3 </view>
4 <we-canvas id="poster" bind:success="onCreateSuccess" bind:fail="onCreateFail"/>
...\ No newline at end of file ...\ No newline at end of file
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
...@@ -15,7 +15,7 @@ Page({ ...@@ -15,7 +15,7 @@ Page({
15 name: "", 15 name: "",
16 phone: "", 16 phone: "",
17 messageContant: "", // 单词拼写错误 17 messageContant: "", // 单词拼写错误
18 18 userInfo: {},
19 19
20 }, 20 },
21 onShareAppMessage() {}, 21 onShareAppMessage() {},
...@@ -31,7 +31,31 @@ Page({ ...@@ -31,7 +31,31 @@ Page({
31 // this.setData({ 31 // this.setData({
32 // commonTipsCompVisible: false 32 // commonTipsCompVisible: false
33 // }) 33 // })
34 this.queryMember().then((result) => {
35 this.setData({
36 name:result.nickname
37 })
38 });
34 }, 39 },
40
41 /**
42 * 获取会员信息
43 */
44 queryMember() {
45 return new Promise((resolve, reject) => {
46 app.post({
47 url: app.api.member,
48 data: {}
49 }).then((result) => {
50 this.setData({
51 userInfo: result
52 })
53 resolve(result);
54 })
55 });
56 },
57
58
35 /** 59 /**
36 * 提交表单 60 * 提交表单
37 */ 61 */
...@@ -55,14 +79,14 @@ Page({ ...@@ -55,14 +79,14 @@ Page({
55 }) 79 })
56 return; 80 return;
57 } 81 }
58 if (!phone) { 82 // if (!phone) {
59 wx.showToast({ 83 // wx.showToast({
60 title: "请输入联系方式", 84 // title: "请输入联系方式",
61 icon: 'none' 85 // icon: 'none'
62 }) 86 // })
63 return; 87 // return;
64 } 88 // }
65 if (!checkMobile(phone)) { 89 if (phone && !checkMobile(phone)) {
66 wx.showToast({ 90 wx.showToast({
67 title: "请输入正确联系方式", 91 title: "请输入正确联系方式",
68 icon: 'none' 92 icon: 'none'
......
...@@ -8,11 +8,11 @@ ...@@ -8,11 +8,11 @@
8 <view class="form"> 8 <view class="form">
9 <view class="form-item"> 9 <view class="form-item">
10 <view class="label">用户姓名</view> 10 <view class="label">用户姓名</view>
11 <input class="val" bindinput="bindNameInput" placeholder="请输入姓名" /> 11 <input value="{{name}}" class="val" bindinput="bindNameInput" placeholder="请输入姓名" />
12 </view> 12 </view>
13 <view class="form-item"> 13 <view class="form-item">
14 <view class="label">联系方式</view> 14 <view class="label">联系方式</view>
15 <input class="val" bindinput="bindPhoneInput" placeholder="请输入联系方式" type="number" /> 15 <input class="val" bindinput="bindPhoneInput" placeholder="选填" type="number" />
16 </view> 16 </view>
17 <textarea class="textarea" bindinput="bindMessageContantInput" maxlength="500" placeholder="不超过500字"></textarea> 17 <textarea class="textarea" bindinput="bindMessageContantInput" maxlength="500" placeholder="不超过500字"></textarea>
18 <!-- 图片上传 --> 18 <!-- 图片上传 -->
......
1 import QR from '../../utils/qrcode' 1 import QR from '../../utils/qrcode'
2 import Poster from '../../miniprogram_dist/poster/poster';
2 3
3 let app = getApp(); 4 let app = getApp();
4 Page({ 5 Page({
...@@ -18,10 +19,115 @@ Page({ ...@@ -18,10 +19,115 @@ Page({
18 this.initData(); 19 this.initData();
19 }, 20 },
20 initData() { 21 initData() {
22 // 获取用户信息 小程序码
21 this.queryMember().then((result) => { 23 this.queryMember().then((result) => {
24 // 获取海报数据
25 let posterData = this.getPosterConfig();
26 // 绘制设置海报
27 this.onCreatePoster(posterData);
28 });
29 },
30
31 onPosterSuccess(e) {
32 wx.hideLoading();
33 const {
34 detail
35 } = e;
36 console.log("detail:", detail)
37 this.setData({
38 imageUrl: detail
39 })
40 },
41 onPosterFail(err) {
42 wx.hideLoading();
43 console.error(err);
44 },
22 45
46 /**
47 * 异步生成海报
48 */
49 onCreatePoster(posterConfig) {
50 console.log("posterConfig:", posterConfig);
51 this.setData({
52 posterConfig: posterConfig
53 }, () => {
54 Poster.create(true); // 入参:true为抹掉重新生成
23 }); 55 });
24 }, 56 },
57
58 // 获取海报数据
59 getPosterConfig() {
60 // 合成图片需要的数据
61 let {
62 userInfo
63 } = this.data;
64
65 let blocks = [{
66 x: 0,
67 y: 0,
68 width: 690,
69 height: 900,
70 backgroundColor: "#ffffff",
71 borderRadius: 10,
72 }];
73 let images = [{
74 x: 286,
75 y: 30,
76 width: 120,
77 height: 120,
78 borderRadius: 120,
79 zIndex: 11,
80 url: userInfo.avatar,
81 }, {
82 x: 126,
83 y: 220,
84 width: 440,
85 height: 440,
86 zIndex: 11,
87 url: userInfo.memberUrl,
88 }];
89 let lines = [];
90 let texts = [{
91 x: 690 / 2,
92 y: 192,
93 width: 690,
94 fontSize: 36,
95 color: "#3680EB",
96 textAlign: "center",
97 zIndex: 11,
98 text: userInfo.nickname,
99 }, {
100 x: 690 / 2,
101 y: 720,
102 width: 690,
103 fontSize: 28,
104 color: "#666666",
105 textAlign: "center",
106 zIndex: 11,
107 text: "深士照明",
108 }, {
109 x: 690 / 2,
110 y: 800,
111 width: 690,
112 fontSize: 28,
113 color: "#666666",
114 textAlign: "center",
115 zIndex: 11,
116 text: "扫码即获专属积分,兑换超值奖品",
117 }];
118
119 let posterData = {
120 width: 690,
121 height: 900,
122 debug: false,
123 blocks: blocks,
124 images: images,
125 lines: lines,
126 texts: texts,
127 }
128 return posterData;
129 },
130
25 /** 131 /**
26 * 获取会员信息 132 * 获取会员信息
27 */ 133 */
...@@ -37,73 +143,15 @@ Page({ ...@@ -37,73 +143,15 @@ Page({
37 userInfo: userInfo, 143 userInfo: userInfo,
38 qrImagePath: userInfo.memberUrl, 144 qrImagePath: userInfo.memberUrl,
39 }) 145 })
40
41 wx.downloadFile({
42 url: userInfo.memberUrl,
43 success(res) {
44 if (res.statusCode === 200) {
45 _this.setData({
46 imageUrl: res.tempFilePath
47 })
48 }
49 }
50 })
51
52 // 生成个人二维码
53 // let tlMemberCode = userInfo.memberCode;
54 // let qrSize = this.setCanvasSize(440);
55 // let codeContent = `tlMemberCode=${tlMemberCode}`
56 // this.createQrCode(codeContent, 'qrcanvas', qrSize.w, qrSize.h);
57
58 resolve(); 146 resolve();
59 }) 147 })
60 }); 148 });
61 }, 149 },
62 150
63 createQrCode(content, canvasId, cavW, cavH) {
64 //调用插件中的draw方法,绘制二维码图片
65 QR.api.draw(content, canvasId, cavW, cavH);
66 this.canvasToTempImage(canvasId);
67 },
68 //获取临时缓存图片路径,存入data中
69 canvasToTempImage(canvasId) {
70 let that = this;
71 wx.canvasToTempFilePath({
72 canvasId, // 这里canvasId即之前创建的canvas-id
73 success: function (res) {
74 let tempFilePath = res.tempFilePath;
75 console.log(tempFilePath);
76 that.setData({ // 如果采用mpvue,即 this.imagePath = tempFilePath
77 qrImagePath: tempFilePath,
78 });
79 },
80 fail: function (res) {
81 console.log(res);
82 }
83 });
84 },
85 //适配不同屏幕大小的canvas
86 setCanvasSize(sz) {
87 var size = {};
88 try {
89 var res = wx.getSystemInfoSync();
90 var scale = 750 / sz; //不同屏幕下canvas的适配比例;设计稿是750宽
91 var width = res.windowWidth / scale;
92 var height = width; //canvas画布为正方形
93 size.w = width;
94 size.h = height;
95 } catch (e) {
96 // Do something when catch error
97 console.log("获取设备信息失败" + e);
98 }
99 return size;
100 },
101
102 /** 151 /**
103 * 保存图片到本地 152 * 保存图片到本地
104 */ 153 */
105 saveImageToPhotosAlbum() { 154 saveImageToPhotosAlbum() {
106 console.log("saveImageToPhotosAlbum");
107 let _this = this; 155 let _this = this;
108 if (!_this.data.imageUrl) { 156 if (!_this.data.imageUrl) {
109 wx.showToast({ 157 wx.showToast({
...@@ -143,4 +191,52 @@ Page({ ...@@ -143,4 +191,52 @@ Page({
143 } 191 }
144 }) 192 })
145 }, 193 },
194
195
196
197
198
199
200
201 // 创建二维码
202 createQrCode(content, canvasId, cavW, cavH) {
203 //调用插件中的draw方法,绘制二维码图片
204 QR.api.draw(content, canvasId, cavW, cavH);
205 this.canvasToTempImage(canvasId);
206 },
207 //获取临时缓存图片路径,存入data中
208 canvasToTempImage(canvasId) {
209 let that = this;
210 wx.canvasToTempFilePath({
211 canvasId, // 这里canvasId即之前创建的canvas-id
212 success: function (res) {
213 let tempFilePath = res.tempFilePath;
214 console.log(tempFilePath);
215 that.setData({ // 如果采用mpvue,即 this.imagePath = tempFilePath
216 qrImagePath: tempFilePath,
217 });
218 },
219 fail: function (res) {
220 console.log(res);
221 }
222 });
223 },
224 //适配不同屏幕大小的canvas
225 setCanvasSize(sz) {
226 var size = {};
227 try {
228 var res = wx.getSystemInfoSync();
229 var scale = 750 / sz; //不同屏幕下canvas的适配比例;设计稿是750宽
230 var width = res.windowWidth / scale;
231 var height = width; //canvas画布为正方形
232 size.w = width;
233 size.h = height;
234 } catch (e) {
235 // Do something when catch error
236 console.log("获取设备信息失败" + e);
237 }
238 return size;
239 },
240
241
146 }) 242 })
......
1 { 1 {
2 "usingComponents": {
3 "poster": "/miniprogram_dist/poster/index"
4 },
2 "navigationBarTitleText": "我的二维码" 5 "navigationBarTitleText": "我的二维码"
3 } 6 }
......
...@@ -24,14 +24,19 @@ $contentWidth:690px; ...@@ -24,14 +24,19 @@ $contentWidth:690px;
24 24
25 .card { 25 .card {
26 margin: 0 auto; 26 margin: 0 auto;
27 padding: 20px 0 74px; 27 // padding: 20px 0 74px;
28 width: $contentWidth; 28 width: $contentWidth;
29 background: #FFFFFF; 29 // background: #FFFFFF;
30 box-shadow: 0 3px 9px 0 rgba(0, 0, 0, 0.10); 30 // box-shadow: 0 3px 9px 0 rgba(0, 0, 0, 0.10);
31 border-radius: 10px; 31 border-radius: 10px;
32 text-align: center; 32 text-align: center;
33 font-size: 32px; 33 font-size: 32px;
34 34
35 .my-card {
36 width: 690px;
37 height: 900px;
38 }
39
35 .avatar { 40 .avatar {
36 width: 120px; 41 width: 120px;
37 height: 120px; 42 height: 120px;
......
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"></view> 3 <view class="app__bgc bgc"></view>
3 <view class="app__bg bg"></view> 4 <view class="app__bg bg"></view>
...@@ -5,13 +6,12 @@ ...@@ -5,13 +6,12 @@
5 <view class="top-space"></view> 6 <view class="top-space"></view>
6 <view class="content"> 7 <view class="content">
7 <view class="card"> 8 <view class="card">
8 <image class="avatar" mode="widthFix" src="{{userInfo.avatar}}" /> 9 <image class="my-card" mode="widthFix" src="{{imageUrl}}" />
10 <!-- <image class="avatar" mode="widthFix" src="{{userInfo.avatar}}" />
9 <view class="nickname">{{userInfo.nickname}}</view> 11 <view class="nickname">{{userInfo.nickname}}</view>
10 <image class="qrcode" mode="widthFix" src="{{qrImagePath}}" /> 12 <image class="qrcode" mode="widthFix" src="{{qrImagePath}}" />
11 <!-- <image wx:if="{{qrImagePath}}" class="qrcode" mode="widthFix" src="{{qrImagePath}}" />
12 <canvas wx:else class="qrcode" style="visibility: hidden;" canvas-id="qrcanvas" /> -->
13 <view class="t1">深士照明</view> 13 <view class="t1">深士照明</view>
14 <view class="t1 t2">扫码即获专属积分,兑换超值奖品</view> 14 <view class="t1 t2">扫码即获专属积分,兑换超值奖品</view> -->
15 </view> 15 </view>
16 <view class="tips">分享邀请好友加入,赢取推广积分</view> 16 <view class="tips">分享邀请好友加入,赢取推广积分</view>
17 <view class="btn-wrap"> 17 <view class="btn-wrap">
......
...@@ -94,8 +94,21 @@ Page({ ...@@ -94,8 +94,21 @@ Page({
94 parentId: "" 94 parentId: ""
95 } 95 }
96 }).then((result) => { 96 }).then((result) => {
97 let provinceList = result;
98 // ---- start 把广东移动到第一条
99 for (var i = 0; i < provinceList.length; i++) {
100 let key = provinceList[i].areaName.indexOf('广东');
101 if (key != -1) {
102 let target = provinceList[i];
103 provinceList.unshift(target);
104 provinceList.splice(i + 1, 1);
105 break;
106 }
107 }
108 // ---- end 把广东移动到第一条
109
97 this.setData({ 110 this.setData({
98 provinceList: result 111 provinceList: provinceList
99 }) 112 })
100 console.log("provinceList:", result); 113 console.log("provinceList:", result);
101 }) 114 })
......