123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- <template>
- <canvas :style="{ width: cv_width + 'px', height: cv_height + 'px' }" :canvas-id="posterId" :id="posterId"
- class="tui-poster__cv"></canvas>
- </template>
- <script>
- // #ifdef MP-WEIXIN
- const posterId = `poster_${Math.ceil(Math.random() * 10e5).toString(36)}`
- // #endif
- export default {
- name: "tui-poster",
- emits: ['ready'],
- props: {
- //海报宽度,单位rpx
- width: {
- type: [Number, String],
- default: 800
- },
- //海报高度,单位rpx
- height: {
- type: [Number, String],
- default: 1200
- },
- //像素比
- pixel: {
- type: [Number, String],
- default: 3
- }
- },
- watch: {
- width(val) {
- this.cv_width = this.getPX(this.width)
- },
- height(val) {
- this.cv_height = this.getPX(this.height)
- }
- },
- data() {
- // #ifndef MP-WEIXIN
- const posterId = `poster_${Math.ceil(Math.random() * 10e5).toString(36)}`
- // #endif
- return {
- posterId: posterId,
- cv_width: 400,
- cv_height: 600
- };
- },
- created() {
- this.cv_width = this.getPX(this.width)
- this.cv_height = this.getPX(this.height)
- this.ctx = null
- },
- mounted() {
- setTimeout(() => {
- this.ctx = uni.createCanvasContext(this.posterId, this)
- this.$emit('ready', {})
- }, 50)
- },
- methods: {
- toast(msg) {
- uni.showToast({
- title: msg,
- icon: 'none'
- })
- },
- getPX(val) {
- return uni.upx2px(Number(val) * Number(this.pixel))
- },
- getTextWidth(context, text, fontSize) {
- let width = 0;
- // #ifndef MP-ALIPAY || MP-BAIDU
- width = context.measureText(text).width
- // #endif
- // #ifdef MP-ALIPAY || MP-BAIDU
- let sum = 0;
- for (let i = 0, len = text.length; i < len; i++) {
- if (text.charCodeAt(i) >= 0 && text.charCodeAt(i) <= 255)
- sum = sum + 1;
- else
- sum = sum + 2;
- }
- width = sum / 2 * this.getPX(fontSize)
- // #endif
- return width
- },
- getWrapText(text, fontSize, textWidth, width, ctx, rows = 2) {
- let textArr = [];
- if (textWidth > width) {
- let fillText = '';
- let lines = 1;
- let arr = text.split('')
- for (let i = 0, len = arr.length; i < len; i++) {
- fillText = fillText + arr[i];
- if (this.getTextWidth(ctx, fillText, fontSize) >= width) {
- if (lines === rows && rows !== -1) {
- if (i !== arr.length - 1) {
- fillText = fillText.substring(0, fillText.length - 1) + '...';
- }
- textArr.push(fillText);
- break;
- }
- textArr.push(fillText);
- fillText = '';
- lines++;
- } else if (i === arr.length - 1) {
- textArr.push(fillText);
- }
- }
- } else {
- textArr.push(text)
- }
- return textArr;
- },
- startDrawText(ctx, param) {
- let styles = param.style || {}
- let {
- left,
- top,
- fontSize,
- color,
- baseLine = 'normal',
- textAlign = 'left',
- frontSize,
- spacing,
- opacity = 1,
- lineThrough = false,
- width = 600,
- rows = 1,
- lineHeight = 0,
- fontWeight = 'normal',
- fontStyle = 'normal',
- fontFamily = "sans-serif"
- } = styles;
- ctx.save();
- ctx.beginPath();
- ctx.font = fontStyle + " " + fontWeight + " " + this.getPX(fontSize) + "px " + fontFamily
- ctx.setGlobalAlpha(opacity);
- // ctx.setFontSize(this.getPX(fontSize));
- ctx.setFillStyle(color);
- ctx.setTextBaseline(baseLine);
- ctx.setTextAlign(textAlign);
- let textWidth = this.getTextWidth(ctx, param.text, fontSize);
- width = this.getPX(width);
- let textArr = this.getWrapText(param.text, fontSize, textWidth, width, ctx, rows)
- if (param.frontText) {
- ctx.setFontSize(this.getPX(frontSize));
- left = this.getTextWidth(ctx, param.frontText, frontSize) + this.getPX(left + spacing);
- ctx.setFontSize(this.getPX(fontSize));
- } else {
- left = this.getPX(left)
- }
- textArr.forEach((item, index) => {
- ctx.fillText(item, left, this.getPX(top + (lineHeight || fontSize) * index))
- })
- ctx.restore();
- if (lineThrough) {
- let lineY = top;
- switch (baseLine) {
- case 'top':
- lineY += fontSize / 2 + 4;
- break;
- case 'middle':
- break;
- case 'bottom':
- lineY -= fontSize / 2 + 4;
- break;
- default:
- // #ifdef MP-WEIXIN
- lineY -= fontSize / 2 - 3;
- // #endif
- // #ifndef MP-WEIXIN
- lineY -= fontSize / 2 - 4;
- // #endif
- break;
- }
- ctx.save();
- ctx.moveTo(left, this.getPX(lineY));
- ctx.lineTo(left + textWidth + 2, this.getPX(lineY));
- ctx.setStrokeStyle(color);
- ctx.stroke();
- ctx.restore();
- }
- },
- drawRadiusRect(ctx, styles) {
- let {
- left,
- top,
- width,
- height,
- borderRadius
- } = styles;
- let r = this.getPX(borderRadius / 2);
- left = this.getPX(left)
- top = this.getPX(top)
- width = this.getPX(width)
- height = this.getPX(height)
- ctx.beginPath();
- ctx.arc(left + r, top + r, r, Math.PI, Math.PI * 1.5);
- ctx.moveTo(left + r, top);
- ctx.lineTo(left + width - r, top);
- ctx.lineTo(left + width, top + r);
- ctx.arc(left + width - r, top + r, r, Math.PI * 1.5, Math.PI * 2);
- ctx.lineTo(left + width, top + height - r);
- ctx.lineTo(left + width - r, top + height);
- ctx.arc(left + width - r, top + height - r, r, 0, Math.PI * 0.5);
- ctx.lineTo(left + r, top + height);
- ctx.lineTo(left, top + height - r);
- ctx.arc(left + r, top + height - r, r, Math.PI * 0.5, Math.PI);
- ctx.lineTo(left, top + r);
- ctx.lineTo(left + r, top);
- },
- startDrawImage(ctx, param) {
- let styles = param.style || {}
- let {
- left,
- top,
- width,
- height,
- borderRadius = 0,
- borderWidth = 0,
- borderColor
- } = styles;
- ctx.save();
- if (borderRadius > 0) {
- this.drawRadiusRect(ctx, styles);
- ctx.strokeStyle = 'rgba(0,0,0,0)'
- // #ifndef MP-BAIDU || MP-TOUTIAO
- ctx.stroke();
- // #endif
- ctx.clip();
- }
- ctx.drawImage(param.src, this.getPX(left), this.getPX(top), this.getPX(width), this.getPX(
- height))
- if (borderWidth && borderWidth > 0) {
- ctx.setStrokeStyle(borderColor);
- ctx.setLineWidth(this.getPX(borderWidth));
- ctx.stroke();
- }
- ctx.restore();
- },
- startDrawRect(ctx, param) {
- let styles = param.style || {}
- let {
- width,
- height,
- left,
- top,
- borderWidth,
- backgroundColor,
- gradientColor,
- gradientType = 1,
- borderColor,
- borderRadius = 0,
- opacity = 1,
- shadow
- } = styles;
- if (backgroundColor) {
- ctx.save();
- ctx.setGlobalAlpha(opacity);
- if (gradientColor) {
- let grd = null;
- if (gradientType == 1) {
- grd = ctx.createLinearGradient(0, 0, this.getPX(width), this.getPX(height))
- } else {
- grd = ctx.createLinearGradient(0, this.getPX(width), this.getPX(height), 0)
- }
- grd.addColorStop(0, backgroundColor)
- grd.addColorStop(1, gradientColor)
- ctx.setFillStyle(grd);
- } else {
- ctx.setFillStyle(backgroundColor);
- }
- if (shadow) {
- const {
- offsetX,
- offsetY,
- blur,
- color
- } = shadow;
- ctx.shadowOffsetX = this.getPX(offsetX)
- ctx.shadowOffsetY = this.getPX(offsetY)
- ctx.shadowBlur = blur
- ctx.shadowColor = color
- }
- if (borderRadius > 0) {
- this.drawRadiusRect(ctx, styles);
- ctx.fill();
- } else {
- ctx.fillRect(this.getPX(left), this.getPX(top), this.getPX(width), this.getPX(height));
- }
- ctx.restore();
- }
- if (borderWidth) {
- ctx.save();
- ctx.setGlobalAlpha(opacity);
- ctx.setStrokeStyle(borderColor);
- ctx.setLineWidth(this.getPX(borderWidth));
- if (borderRadius > 0) {
- this.drawRadiusRect(ctx, styles);
- ctx.stroke();
- } else {
- ctx.strokeRect(this.getPX(left), this.getPX(top), this.getPX(width), this.getPX(height));
- }
- ctx.restore();
- }
- },
- startDrawLine(ctx, param) {
- let styles = param.style
- let {
- left,
- top,
- endLeft,
- endTop,
- color,
- width = 1
- } = styles;
- ctx.save();
- ctx.beginPath();
- ctx.setStrokeStyle(color);
- ctx.setLineWidth(this.getPX(width));
- ctx.moveTo(this.getPX(left), this.getPX(top));
- ctx.lineTo(this.getPX(endLeft), this.getPX(endTop));
- ctx.stroke();
- ctx.closePath();
- ctx.restore();
- },
- 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
- },
- 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];
- result = 1
- }
- for (var i = 0; i < resultObj.deniedPresent.length; i++) {
- var deniedPresentPermission = resultObj.deniedPresent[i];
- result = 0
- }
- for (var i = 0; i < resultObj.deniedAlways.length; i++) {
- var deniedAlwaysPermission = resultObj.deniedAlways[i];
- result = -1
- }
- resolve(result);
- },
- function(error) {
- 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("app-settings:");
- application2.openURL(setting2);
- plus.ios.deleteObject(setting2);
- plus.ios.deleteObject(NSURL2);
- plus.ios.deleteObject(application2);
- } else {
- 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
- },
- judgePermissionPhotoLibrary: async function(callback) {
- // #ifndef APP-PLUS || MP-WEIXIN || MP-QQ
- callback && callback(true)
- // #endif
- // #ifdef APP-PLUS
- const res = uni.getSystemInfoSync();
- let result;
- let isAndroid = res.platform.toLocaleLowerCase() == "android";
- if (isAndroid) {
- result = await this.requestAndroidPermission('android.permission.WRITE_EXTERNAL_STORAGE')
- } else {
- result = this.judgeIosPermissionPhotoLibrary()
- }
- if (result == 1) {
- callback && callback(true)
- } else {
- if (!(!isAndroid && result == -1)) {
- uni.showModal({
- title: '提示',
- content: '您还没有开启相册权限,是否立即开启?',
- showCancel: true,
- success: (res) => {
- if (res.confirm) {
- this.gotoAppPermissionSetting(isAndroid)
- }
- }
- })
- } else {
- callback && callback(true)
- }
- }
- // #endif
- // #ifdef MP-WEIXIN || MP-QQ
- uni.authorize({
- scope: 'scope.writePhotosAlbum',
- success() {
- callback && callback(true)
- },
- fail() {
- uni.showModal({
- title: '提示',
- content: '您还没有开启相册权限,是否立即开启?',
- showCancel: true,
- success: (res) => {
- if (res.confirm) {
- wx.openSetting({
- success(res) {}
- });
- }
- }
- })
- }
- })
- // #endif
- },
- imgDownload(url) {
- return new Promise((resolve, reject) => {
- uni.downloadFile({
- url: url,
- success: res => {
- resolve(res.tempFilePath);
- },
- fail: err => {
- reject(false)
- }
- })
- })
- },
- base64ToImg(base64) {
- const uniqueId = `poster_${Math.ceil(Math.random() * 10e5).toString(36)}`
- return new Promise((resolve, reject) => {
- // #ifdef MP-WEIXIN
- const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
- let arrayBuffer = wx.base64ToArrayBuffer(bodyData)
- const filePath = `${wx.env.USER_DATA_PATH}/${uniqueId}.${format}`;
- wx.getFileSystemManager().writeFile({
- filePath,
- data: arrayBuffer,
- encoding: 'binary',
- success() {
- resolve(filePath);
- },
- fail() {
- reject(false)
- }
- })
- // #endif
- // #ifdef APP-PLUS
- let bitmap = new plus.nativeObj.Bitmap(uniqueId);
- bitmap.loadBase64Data(base64, function() {
- bitmap.save(`_doc/${uniqueId}.png`, {}, function(e) {
- let target = e.target;
- resolve(target);
- }, function(e) {
- reject(false)
- });
- }, function() {
- reject(false)
- });
- // #endif
- // #ifdef H5
- resolve(base64);
- // #endif
- // #ifndef MP-WEIXIN || APP-PLUS || H5
- reject(false)
- // #endif
- })
- },
- startDraw(data, callback) {
- let ctx = this.ctx
- if (ctx) {
- ctx.clearRect(0, 0, this.cv_width, this.cv_height)
- data.forEach((item) => {
- if (item.type === 'image') {
- this.startDrawImage(ctx, item)
- } else if (item.type === 'text') {
- this.startDrawText(ctx, item)
- } else if (item.type === 'rect') {
- this.startDrawRect(ctx, item)
- } else if (item.type === 'line') {
- this.startDrawLine(ctx, item)
- }
- });
- const platform = uni.getSystemInfoSync().platform;
- let time = 80;
- if (platform === 'android') {
- time = 300;
- }
- setTimeout(() => {
- ctx.draw(false, () => {
- setTimeout(() => {
- // #ifdef MP-ALIPAY
- ctx.toTempFilePath({
- success: res => {
- callback && callback(res.apFilePath)
- },
- fail: err => {
- callback && callback(false)
- }
- });
- // #endif
- // #ifndef MP-ALIPAY
- uni.canvasToTempFilePath({
- x: 0,
- y: 0,
- canvasId: this.posterId,
- fileType: 'png',
- quality: 1,
- success: function(res) {
- callback && callback(res.tempFilePath)
- },
- fail() {
- callback && callback(false)
- }
- }, this)
- // #endif
- }, time)
- })
- }, 50)
- } else {
- callback && callback(false)
- }
- },
- draw(data, callback) {
- // text(文本)、image(图片)、rect(矩形),line(线条)
- // {
- // type:'image',
- // src:'',
- // style:{
- // }
- // }
- if (!data || data.length === 0) return;
- let func = [],
- idxes = [];
- data.forEach((item, index) => {
- if (item.type === 'image') {
- //图片类型:1-本地图片(需要平台支持);2-网络图片; 3- base64 图片(仅App,微信小程序,H5支持)
- if (item.imgType == 2) {
- func.push(this.imgDownload(item.src))
- idxes.push(index)
- }
- // #ifdef APP-PLUS || H5 || MP-WEIXIN
- if (item.imgType == 3) {
- func.push(this.base64ToImg(item.src))
- idxes.push(index)
- }
- // #endif
- }
- })
- if (func.length > 0) {
- Promise.all(func).then(res => {
- res.forEach((imgRes, idx) => {
- let item = data[idxes[idx]]
- item.src = imgRes
- })
- this.startDraw(data, callback)
- }).catch(err => {
- console.log(err)
- this.toast('图片处理失败!')
- })
- } else {
- this.startDraw(data, callback)
- }
- },
- save(file) {
- // #ifdef H5
- //H5无法直接保存到相册,预览后长按保存
- uni.previewImage({
- urls: [file]
- });
- // #endif
- // #ifndef H5
- this.judgePermissionPhotoLibrary((res) => {
- if (res) {
- uni.saveImageToPhotosAlbum({
- filePath: file,
- success: (res) => {
- this.toast('图片已保存到相册')
- },
- fail: (res) => {
- this.toast('图片保存失败')
- }
- })
- }
- })
- // #endif
- }
- }
- }
- </script>
- <style scoped>
- .tui-poster__cv {
- position: fixed;
- left: -9999px;
- bottom: 0;
- }
- </style>
|