tui-poster.js
14.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
const poster = {
/**
* 绘制分享海报
* @param canvasId
* @param winWidth 宽度
* @param winHeight 高度
* @param imgs 主图以及二维码图
* 格式 {mainPic: mainPic,qrcode: qrcode}
* @param text 描述文字
* @param name 程序名称
**/
drawPoster(canvasId, winWidth, winHeight, imgs, text, name, callback) {
//获取绘图上下文 context
let context = uni.createCanvasContext(canvasId)
// 海报背景
const grd = context.createLinearGradient(0, winWidth, winHeight, 0)
grd.addColorStop(0, '#FFCB4B')
grd.addColorStop(1, '#FFAE34')
// Fill with gradient
context.setFillStyle(grd);
context.fillRect(0, 0, winWidth, winHeight);
// 主图
let width = uni.upx2px(648),
height = uni.upx2px(774);
context.drawImage(imgs.mainPic, (winWidth - width) / 2, uni.upx2px(60), width, height)
let a = uni.upx2px(40) //定义行高
context.setFontSize(uni.upx2px(30))
context.setFillStyle("#f00")
//名称很长调用方法将文字折行,传参 文字内容text,画布context
let w = poster.getTextWidth(text, context) || uni.upx2px(550) * 2
let row = poster.wrapText(text, Math.floor(w / 2), context)
for (let i = 0; i < row.length; i++) {
context.fillText(row[i], (winWidth - w / 2) / 2, (winHeight + uni.upx2px(460)) / 2 + a * i)
}
// 识别小程序二维码
context.setFontSize(uni.upx2px(36))
let w1 = poster.getTextWidth(name, context) || 140
let x = (winWidth - w1 - 20 - 80) / 2;
context.drawImage(imgs.qrcode, x, (winHeight + uni.upx2px(760)) / 2, 80, 80)
context.setFillStyle("#152338")
context.fillText(name, x + 80 + 20, (winHeight + uni.upx2px(890)) / 2)
context.setFillStyle("#333")
context.setFontSize(uni.upx2px(24))
context.fillText('长按识别·立即体验', x + 80 + 20, (winHeight + uni.upx2px(990)) / 2)
context.draw(false, () => {
poster.createPoster(canvasId, winWidth, winHeight, (res) => {
callback && callback(res)
})
})
},
/**
*绘制商品海报
* @param canvasId
* @param winWidth 宽度
* @param winHeight 高度
* @param imgs 主图以及二维码图
* 格式 {mainPic: mainPic,qrcode: qrcode}
* @param text 商品名称
* @param price 价格 格式 12.00,10.50
* @param originalPrice 原价 格式 12.00,10.50
* @param name 程序名称
**/
drawGoodsPoster(canvasId, winWidth, winHeight, imgs, text, price, originalPrice, name, callback) {
let scaleRatio = 2
//获取绘图上下文 context
let context = uni.createCanvasContext(canvasId)
// 海报背景
const grd = context.createLinearGradient(0, winWidth, winHeight, 0)
grd.addColorStop(0, '#FFFFFF')
grd.addColorStop(1, '#FFFFFF')
// Fill with gradient
context.setFillStyle(grd);
context.fillRect(0, 0, winWidth, winHeight);
// 主图
let width = uni.upx2px(500 * scaleRatio),
height = uni.upx2px(500 * scaleRatio);
context.drawImage(imgs.mainPic, (winWidth - width) / 2, uni.upx2px(30 * scaleRatio), width, height)
let a = uni.upx2px(40 * scaleRatio) //定义行高
context.setFontSize(uni.upx2px(30 * scaleRatio))
context.setFillStyle("#343434")
let w = uni.upx2px(468 * scaleRatio)
//名称很长调用方法将文字折行,传参 文字内容text,画布context
let row = poster.wrapText(text, Math.floor(w), context, 2)
for (let i = 0; i < row.length; i++) {
context.fillText(row[i], uni.upx2px(30 * scaleRatio), uni.upx2px(580 * scaleRatio) + a * i)
}
context.setFillStyle("#EB0909")
context.setFontSize(uni.upx2px(26 * scaleRatio))
context.fillText('¥', uni.upx2px(30 * scaleRatio), uni.upx2px(680 * scaleRatio))
let priceArr = Number(price).toFixed(2).toString().split('.')
context.setFillStyle("#EB0909")
context.setFontSize(uni.upx2px(36 * scaleRatio))
context.fillText(priceArr[0], uni.upx2px(56 * scaleRatio), uni.upx2px(680 * scaleRatio))
let w1 = poster.getTextWidth(priceArr[0], context) || 35
context.setFontSize(uni.upx2px(26 * scaleRatio))
context.setFillStyle("#EB0909")
context.fillText(`.${priceArr[1]}`, uni.upx2px(60 * scaleRatio) + w1, uni.upx2px(680 * scaleRatio))
context.setFillStyle("#999999")
context.setFontSize(uni.upx2px(24 * scaleRatio))
let w2 = uni.upx2px(76 * scaleRatio) + w1 + (poster.getTextWidth(`.${priceArr[1]}`, context) || 32)
context.fillText(`¥${originalPrice}`, w2, uni.upx2px(680 * scaleRatio))
context.moveTo(w2, uni.upx2px(672 * scaleRatio))
context.lineTo((w2 + 50 * scaleRatio), uni.upx2px(672 * scaleRatio))
context.setStrokeStyle('#999999')
context.stroke(); //对当前路径进行描边
// 识别小程序二维码
let x = winWidth - uni.upx2px(46 + 130) * scaleRatio;
context.drawImage(imgs.qrcode, x, uni.upx2px(735 * scaleRatio), uni.upx2px(130 * scaleRatio), uni.upx2px(130 *
scaleRatio))
context.setFillStyle("#333")
context.setFontSize(uni.upx2px(32 * scaleRatio))
context.fillText(name, uni.upx2px(40 * scaleRatio), uni.upx2px(780 * scaleRatio))
context.setFontSize(uni.upx2px(24 * scaleRatio))
context.fillText('长按识别·立即体验', uni.upx2px(40 * scaleRatio), uni.upx2px(835 * scaleRatio))
context.draw(false, () => {
poster.createPoster(canvasId, winWidth, winHeight, (res) => {
callback && callback(res)
})
})
},
//生成海报图片(分享所需图片)
createPoster(canvasId, winWidth, winHeight, callback) {
// let scaleRatio= uni.getSystemInfoSync().scaleRatio
// #ifdef MP-ALIPAY
let context = uni.createCanvasContext(canvasId)
context.toTempFilePath({
success: res => {
callback && callback(res.apFilePath)
},
fail: err => {
callback && callback(false)
}
});
// #endif
// #ifndef MP-ALIPAY
uni.canvasToTempFilePath({
x: 0,
y: 0,
// width: winWidth * scaleRatio,
// height: Math.round(winHeight * scaleRatio),
// destWidth: winWidth * scaleRatio,
// destHeight: Math.round(winHeight) * scaleRatio,
canvasId: canvasId,
fileType: 'png',
quality: 1,
success: function(res) {
callback && callback(res.tempFilePath)
},
fail() {
callback && callback(false)
}
})
// #endif
},
//ios用户拒绝相册访问 ,引导用户到设置页面,开启相册访问权限
//-1=未请求 1 = 已允许,0 = 拒绝|受限
judgeIosPermissionPhotoLibrary() {
// #ifdef APP-PLUS
var result = 0;
var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary");
var authStatus = PHPhotoLibrary.authorizationStatus();
if (authStatus === 0) {
result = -1;
} else if (authStatus == 3) {
result = 1;
console.log("相册权限已经开启");
} else {
result = 0;
console.log("相册权限没有开启");
}
plus.ios.deleteObject(PHPhotoLibrary);
return result;
// #endif
},
// Android权限查询
requestAndroidPermission(permissionID) {
// #ifdef APP-PLUS
return new Promise((resolve, reject) => {
plus.android.requestPermissions(
// 理论上支持多个权限同时查询,本函数封装只处理了一个权限的情况。有需要的可自行扩展封装
[permissionID],
function(resultObj) {
var result = 0;
for (var i = 0; i < resultObj.granted.length; i++) {
var grantedPermission = resultObj.granted[i];
console.log('已获取的权限:' + grantedPermission);
result = 1
}
for (var i = 0; i < resultObj.deniedPresent.length; i++) {
var deniedPresentPermission = resultObj.deniedPresent[i];
console.log('拒绝本次申请的权限:' + deniedPresentPermission);
result = 0
}
for (var i = 0; i < resultObj.deniedAlways.length; i++) {
var deniedAlwaysPermission = resultObj.deniedAlways[i];
console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
result = -1
}
resolve(result);
// 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限
},
function(error) {
console.log('申请权限错误:' + error.code + " = " + error.message);
resolve({
code: error.code,
message: error.message
});
}
);
});
// #endif
},
// 跳转到**应用**的权限页面
gotoAppPermissionSetting(isAndroid) {
// #ifdef APP-PLUS
if (!isAndroid) {
var UIApplication = plus.ios.import("UIApplication");
var application2 = UIApplication.sharedApplication();
var NSURL2 = plus.ios.import("NSURL");
// var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");
var setting2 = NSURL2.URLWithString("app-settings:");
application2.openURL(setting2);
plus.ios.deleteObject(setting2);
plus.ios.deleteObject(NSURL2);
plus.ios.deleteObject(application2);
} else {
// console.log(plus.device.vendor);
var Intent = plus.android.importClass("android.content.Intent");
var Settings = plus.android.importClass("android.provider.Settings");
var Uri = plus.android.importClass("android.net.Uri");
var mainActivity = plus.android.runtimeMainActivity();
var intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
intent.setData(uri);
mainActivity.startActivity(intent);
}
// #endif
},
modal: function(callback, confirmColor, confirmText) {
uni.showModal({
title: '提示',
content: '您还没有开启相册权限,是否立即设置?',
showCancel: true,
cancelColor: "#555",
confirmColor: "#EB0909",
confirmText: "确定",
success(res) {
if (res.confirm) {
callback && callback(true)
} else {
callback && callback(false)
}
}
})
},
//相册权限查询,如果没有权限则提示打开设置页面
judgePermissionPhotoLibrary: async function(callback) {
// #ifdef H5 || MP-ALIPAY
//H5端不支持调用api保存到相册
callback && callback(true)
// #endif
// #ifdef APP-PLUS
const res = uni.getSystemInfoSync();
let result;
let isAndroid = res.platform.toLocaleLowerCase() == "android";
if (isAndroid) {
result = await poster.requestAndroidPermission('android.permission.WRITE_EXTERNAL_STORAGE')
} else {
result = poster.judgeIosPermissionPhotoLibrary()
}
if (result == 1) {
callback && callback(true)
} else {
if (!(!isAndroid && result == -1)) {
poster.modal((res) => {
if (res) {
poster.gotoAppPermissionSetting(isAndroid)
}
})
} else {
callback && callback(true)
}
}
// #endif
// #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU
uni.authorize({
scope: 'scope.writePhotosAlbum',
success() {
callback && callback(true)
},
fail() {
poster.modal((res) => {
if (res) {
uni.openSetting({
success(res) {
console.log(res.authSetting)
}
});
}
})
}
})
// #endif
},
// 将海报图片保存到本地
saveImage(file) {
//检查是否授权相册权限
poster.judgePermissionPhotoLibrary((res) => {
//保存图片
if (res) {
uni.saveImageToPhotosAlbum({
filePath: file,
success(res) {
uni.showToast({
title: '图片保存成功',
icon: 'none'
})
},
fail(res) {
uni.showToast({
title: '图片保存失败',
icon: 'none'
})
}
})
}
})
},
//获取文本宽度(请先查看支持平台,App 端 2.8.12+ 支持)
getTextWidth(text, context) {
const metrics = context.measureText(text)
return metrics.width || 0
},
//图片转成本地文件[同步执行]
async getImage(url) {
return await new Promise((resolve, reject) => {
uni.downloadFile({
url: url,
success: res => {
resolve(res.tempFilePath);
},
fail: res => {
reject(false)
}
})
})
},
//canvas多文字换行
wrapText(text, width, context, rows = 2) {
let txtArr = text.split('')
let temp = ''
let row = []
for (let i = 0, len = txtArr.length; i < len; i++) {
if (context.measureText(temp).width < width) {
temp += txtArr[i]
} else {
i--
row.push(temp)
temp = ''
}
}
row.push(temp)
if (row.length > rows) {
let rowCut = row.slice(0, rows);
let rowPart = rowCut[rows - 1];
let test = "";
let empty = [];
for (let j = 0, length = rowPart.length; j < length; j++) {
if (context.measureText(test).width < width - 20) {
test += rowPart[j];
} else {
break;
}
}
empty.push(test);
let group = empty[0] + "...";
rowCut.splice(rows - 1, 1, group);
row = rowCut;
}
return row
},
//删除已缓存文件,防止超出存储空间大小限制[同步执行]
async removeSavedFile() {
//使用前请先查看支持平台(其他方案:也可以先渲染出图片,当图片加载完成时执行相关方法)
let count = 0;
return await new Promise((resolve, reject) => {
uni.getSavedFileList({
success(res) {
// console.log(res)
count = res.fileList.length;
if (count > 0) {
let num = 0;
let list = res.fileList || []
list.forEach(item => {
// console.log("执行删除文件。。。")
uni.removeSavedFile({
filePath: item.filePath,
complete(res) {
num++;
if (num === count) {
resolve(true)
}
}
})
})
} else {
resolve(true)
}
},
fail() {
reject(false)
console.log("执行删除文件失败")
}
})
})
},
//当服务器端返回图片base64时,转成本地文件[同步执行]
async getImagebyBase64(base64) {
//使用前先查看支持平台
// #ifdef MP-WEIXIN
return await new Promise((resolve, reject) => {
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
let arrayBuffer = wx.base64ToArrayBuffer(bodyData)
//getuuid:注意这里名称需要动态生成(名称相同部分机型会出现写入失败,显示的是上次生成的图片)
const filePath = `${wx.env.USER_DATA_PATH}/${poster.getuuid()}.${format}`;
//此处可能会出现存储空间不足的情况,可清理缓存解决
//fail the maximum size of the file storage limit is exceeded
wx.getFileSystemManager().writeFile({
filePath,
data: arrayBuffer,
encoding: 'binary',
success() {
resolve(filePath);
},
fail() {
reject(false)
}
})
})
// #endif
},
getuuid() {
let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
return (c === 'x' ? (Math.random() * 16) | 0 : 'r&0x3' | '0x8').toString(16);
});
return uuid;
}
}
export default {
drawPoster: poster.drawPoster,
drawGoodsPoster: poster.drawGoodsPoster,
getImage: poster.getImage,
removeSavedFile: poster.removeSavedFile,
getImagebyBase64: poster.getImagebyBase64,
saveImage: poster.saveImage
};