mp_share
This commit is contained in:
parent
8f4d203f71
commit
16d4cc09da
BIN
fonts/muyao.ttf
Normal file
BIN
fonts/muyao.ttf
Normal file
Binary file not shown.
BIN
fonts/shoushuti.ttf
Normal file
BIN
fonts/shoushuti.ttf
Normal file
Binary file not shown.
BIN
fonts/siyuan.otf
Normal file
BIN
fonts/siyuan.otf
Normal file
Binary file not shown.
BIN
fonts/yangrendong.ttf
Normal file
BIN
fonts/yangrendong.ttf
Normal file
Binary file not shown.
@ -20,6 +20,7 @@
|
||||
"body-parser": "^1.19.0",
|
||||
"bson": "^4.0.2",
|
||||
"bunyan": "^1.8.12",
|
||||
"canvas": "^2.5.0",
|
||||
"compression": "^1.7.4",
|
||||
"connect-mongo": "^2.0.3",
|
||||
"cookie-parser": "^1.4.4",
|
||||
@ -29,6 +30,7 @@
|
||||
"express-session": "^1.16.1",
|
||||
"express-validator": "^5.3.1",
|
||||
"file-stream-rotator": "^0.4.1",
|
||||
"form-data": "^2.3.3",
|
||||
"fs-extra": "^8.0.0",
|
||||
"glob": "^7.1.4",
|
||||
"helmet": "^3.18.0",
|
||||
|
@ -1,21 +1,16 @@
|
||||
import { Router } from 'express';
|
||||
import gamesRouter from './games'
|
||||
import settingsRouter from './settings'
|
||||
import platformsRouter from './platforms'
|
||||
import shareRouter from './share'
|
||||
|
||||
|
||||
|
||||
import gamesRouter from './games';
|
||||
import settingsRouter from './settings';
|
||||
import platformsRouter from './platforms';
|
||||
import shareRouter from './share';
|
||||
import mpShareRouter from './mp_share';
|
||||
|
||||
const router = new Router();
|
||||
|
||||
|
||||
router.use('/settings', settingsRouter);
|
||||
router.use('/platforms', platformsRouter);
|
||||
router.use('/share', shareRouter);
|
||||
router.use('/mp_share', mpShareRouter);
|
||||
router.use('/', gamesRouter);
|
||||
|
||||
|
||||
|
||||
|
||||
export default router;
|
||||
|
130
src/controllers/games/mp_share.js
Normal file
130
src/controllers/games/mp_share.js
Normal file
@ -0,0 +1,130 @@
|
||||
import { Router } from 'express';
|
||||
import FormData from 'form-data';
|
||||
import request from 'request';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import painter from '../../utils/painter';
|
||||
import config from '../../../config/config';
|
||||
|
||||
const router = new Router();
|
||||
|
||||
router.get('/test', async (req, res, next) => {
|
||||
var dataBuffer = new Buffer(base64, 'base64');
|
||||
// var imgBuffer = streamifier.createReadStream(dataBuffer);
|
||||
var imgpath = path.join(__dirname, '../../../temp/flower.jpg');
|
||||
console.log(path.join(__dirname, '../../../temp/logo.png'));
|
||||
var formData = {
|
||||
'image-file': fs.createReadStream(imgpath),
|
||||
// 'image-file': dataBuffer,
|
||||
sub_path: '/mp-share/',
|
||||
file_type: 'mp_share'
|
||||
};
|
||||
request.post(
|
||||
{
|
||||
url: 'http://localhost:2333/api/common/upload',
|
||||
formData: formData,
|
||||
headers: {
|
||||
authorization:
|
||||
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Inl1bGl4aW5nIiwiaWF0IjoxNTYwOTIyOTEyLCJleHAiOjE1NjEwMDkzMTJ9.EAVRtbGxBIp4nDaWUAoO0IqGb6OSqDG5a_GXwbfjkBI'
|
||||
}
|
||||
},
|
||||
function(err, res1, body) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
res.send(err);
|
||||
return;
|
||||
}
|
||||
|
||||
fs.unlink(imgpath, function(error) {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
return false;
|
||||
}
|
||||
console.log('删除文件成功');
|
||||
});
|
||||
|
||||
res.send(body);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
router.get('/test1', async (req, res, next) => {
|
||||
const opt = {
|
||||
baseInfo: {
|
||||
width: '250px',
|
||||
height: '736px',
|
||||
fonts: [
|
||||
{
|
||||
name: 'shoushuti',
|
||||
path: 'fonts/shoushuti.ttf'
|
||||
}
|
||||
]
|
||||
},
|
||||
views: [
|
||||
{
|
||||
type: 'image',
|
||||
url:
|
||||
'https://client-1256832210.cos.ap-beijing.myqcloud.com/mp-share/5d09e26a62bb32868763dba3.jpeg',
|
||||
style: {
|
||||
top: '50px',
|
||||
left: '20px',
|
||||
width: '210px', //可选
|
||||
height: '210px', // 可选
|
||||
'border-radius': '105px', // 可选
|
||||
border: '1px solid #fff' // 可选
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text:
|
||||
'人间四月芳菲尽,山寺桃花始盛开。\n长恨春归无觅处,不知转入此中来。\n——白居易·大林寺桃花',
|
||||
style: {
|
||||
left: '165px',
|
||||
top: '300px',
|
||||
color: '#fae',
|
||||
'max-width': '693px',
|
||||
'font-size': '24px',
|
||||
'font-family': 'shoushuti',
|
||||
'line-height': '50px',
|
||||
'writing-mode': 'vertical-rl'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
try {
|
||||
const imgTempName = await painter(opt);
|
||||
const imgTempPath = path.join(__dirname, '../../../temp/' + imgTempName);
|
||||
// 生成后上传、删除
|
||||
const formData = {
|
||||
'image-file': fs.createReadStream(imgTempPath),
|
||||
sub_path: '/mp-share/',
|
||||
file_type: 'mp_share'
|
||||
};
|
||||
request.post(
|
||||
{
|
||||
url: config.host + '/api/common/upload',
|
||||
formData: formData,
|
||||
headers: {
|
||||
authorization: `${req.headers.authorization}`
|
||||
}
|
||||
},
|
||||
function(uploadErr, uploadRes, uploadBody) {
|
||||
fs.unlink(imgTempPath, function(err) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
if (uploadErr) {
|
||||
next(uploadErr);
|
||||
return;
|
||||
}
|
||||
res.send(uploadBody);
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
@ -34,7 +34,7 @@ const GameShareImage = new mongoose.Schema({
|
||||
ad_count: {type: Number, default: 0},
|
||||
// 广告间隔时间
|
||||
ad_cd: {type: Number, default: 0},
|
||||
// 0: 分享图, 1: 广告
|
||||
// 0: 分享图优先, 1: 广告优先, 2: 只分享, 3: 只广告
|
||||
type: {type: Number, default: 0},
|
||||
// 备注
|
||||
comment: {type: String},
|
||||
|
300
src/utils/painter/index.js
Normal file
300
src/utils/painter/index.js
Normal file
@ -0,0 +1,300 @@
|
||||
const { registerFont, createCanvas, loadImage } = require('canvas');
|
||||
|
||||
const drawRoundRect = require('./round_rect');
|
||||
const wrapText = require('./wrap_text');
|
||||
const fillTextVertical = require('./vertical_text');
|
||||
|
||||
const drawShadow = require('./shadow');
|
||||
const transformBase64 = require('./transfer_base64');
|
||||
|
||||
const config = require('../../../config/config');
|
||||
|
||||
module.exports = async function paint(opt) {
|
||||
// 获取画布基本信息
|
||||
const baseInfo = opt.baseInfo;
|
||||
const views = opt.views;
|
||||
const canvasW = parseInt(baseInfo.width);
|
||||
const canvasH = parseInt(baseInfo.height);
|
||||
|
||||
//引入字体
|
||||
const fonts = baseInfo.fonts;
|
||||
if (fonts.length > 0) {
|
||||
for (let i = 0; i < fonts.length; i++) {
|
||||
const fontName = fonts[i].name;
|
||||
registerFont(fonts[i].path, { family: `${fontName}` });
|
||||
}
|
||||
}
|
||||
|
||||
// 提取图片
|
||||
const imgs = [];
|
||||
views.map((view, index) => {
|
||||
if (view.type === 'image' && view.url) {
|
||||
imgs.push({
|
||||
url: view.url,
|
||||
index: index
|
||||
});
|
||||
}
|
||||
});
|
||||
const imgsInfo = await _absorbImgs(imgs);
|
||||
|
||||
// 创建画布与画笔
|
||||
const canvas = createCanvas(canvasW, canvasH);
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// 画笔功能扩展
|
||||
ctx.wrapText = wrapText;
|
||||
ctx.fillTextVertical = fillTextVertical;
|
||||
ctx.drawRoundRect = drawRoundRect;
|
||||
|
||||
// 绘制背景
|
||||
_drawBg(ctx, baseInfo);
|
||||
|
||||
//绘制元素
|
||||
|
||||
for (let i = 0; i < views.length; i++) {
|
||||
const view = views[i];
|
||||
_drawView(ctx, view, i, imgsInfo);
|
||||
}
|
||||
|
||||
// 生成图片
|
||||
const tempName = `${new Date().getTime()}.png`;
|
||||
const tempPath = config.root + `/temp/${tempName}`;
|
||||
transformBase64(canvas.toDataURL(), tempPath);
|
||||
|
||||
return tempName;
|
||||
};
|
||||
|
||||
function _drawBg(ctx, info) {
|
||||
ctx.save();
|
||||
const w = parseInt(info.width);
|
||||
const h = parseInt(info.height);
|
||||
const bg = info['back-ground'];
|
||||
let bdr = info['border-radius'] || 0;
|
||||
|
||||
// 圆角剪切
|
||||
if (bdr && bdr.endsWith('px')) {
|
||||
bdr = parseInt(bdr);
|
||||
} else if (bdr && bdr.endsWith('%')) {
|
||||
bdr = (w * parseInt(bdr)) / 100;
|
||||
}
|
||||
ctx.drawRoundRect(0, 0, w, h, bdr, false, false);
|
||||
ctx.clip();
|
||||
|
||||
if (!bg) {
|
||||
// 默认背景
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
} else if (
|
||||
bg.startsWith('#') ||
|
||||
bg.startsWith('rgba') ||
|
||||
bg.startsWith('rgb') ||
|
||||
bg.toLowerCase() === 'transparent'
|
||||
) {
|
||||
// 纯色填充
|
||||
ctx.fillStyle = bg;
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
}
|
||||
// TODO: 渐变填充
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function _drawView(ctx, view, index, imgsInfo) {
|
||||
switch (view.type) {
|
||||
case 'image':
|
||||
_drawImg(ctx, view, index, imgsInfo);
|
||||
break;
|
||||
case 'text':
|
||||
_drawText(ctx, view);
|
||||
break;
|
||||
case 'rect':
|
||||
_drawRect(ctx, view);
|
||||
break;
|
||||
// case 'qrcode':
|
||||
// _drawQRCode(ctx, view);
|
||||
// break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(100, 75, 50, 0, 2 * Math.PI);
|
||||
}
|
||||
|
||||
function _drawImg(ctx, view, index, imgsInfo) {
|
||||
const img = imgsInfo[index];
|
||||
const style = view.style;
|
||||
const w = parseInt(style.width);
|
||||
const h = parseInt(style.height);
|
||||
let x = parseInt(style.left);
|
||||
let y = parseInt(style.top);
|
||||
const hasBd = style['border'] ? true : false;
|
||||
let bdr = style['border-radius'] || 0;
|
||||
|
||||
if (!img) return;
|
||||
ctx.save();
|
||||
|
||||
// 圆角剪切
|
||||
if (bdr && bdr.endsWith('px')) {
|
||||
bdr = parseInt(bdr);
|
||||
} else if (bdr && bdr.endsWith('%')) {
|
||||
bdr = (w * parseInt(bdr)) / 100;
|
||||
}
|
||||
|
||||
// 是否旋转
|
||||
if (style['transform']) {
|
||||
ctx.translate(x + w / 2, y + h / 2);
|
||||
const rotateDeg = _rotateDeg(style['transform']);
|
||||
x = -w / 2;
|
||||
y = -h / 2;
|
||||
ctx.rotate(rotateDeg);
|
||||
}
|
||||
|
||||
if (hasBd) {
|
||||
const borderInfo = style['border'].split(' ');
|
||||
ctx.lineWidth = parseInt(borderInfo[0]);
|
||||
ctx.strokeStyle = borderInfo[2];
|
||||
}
|
||||
|
||||
ctx.drawRoundRect(x, y, w, h, bdr, false, hasBd);
|
||||
drawShadow(ctx, style);
|
||||
ctx.clip();
|
||||
|
||||
// 绘画区域比例
|
||||
const cp = w / h;
|
||||
// 原图比例
|
||||
const op = img.width / img.height;
|
||||
if (cp >= op) {
|
||||
const r = img.width / w;
|
||||
const nW = img.width / r;
|
||||
const nH = img.height / r;
|
||||
const nY = y - (nH - h) / 2;
|
||||
ctx.drawImage(img, x, nY, nW, nH);
|
||||
} else {
|
||||
const r = img.height / h;
|
||||
const nW = img.width / r;
|
||||
const nH = img.height / r;
|
||||
const nX = x - (nW - w) / 2;
|
||||
ctx.drawImage(img, nX, y, nW, nH);
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function _drawText(ctx, view) {
|
||||
const style = view.style;
|
||||
let x = parseInt(style.left);
|
||||
let y = parseInt(style.top);
|
||||
const maxWidth = parseInt(style['max-width']);
|
||||
const lineHeight = parseInt(style['line-height']);
|
||||
const fontWeight = parseInt(style['font-weight']) || 500;
|
||||
|
||||
ctx.save();
|
||||
// 字体颜色
|
||||
ctx.fillStyle = style.color || '#000';
|
||||
|
||||
// 字体对齐方式
|
||||
ctx.textAlign = style['text-align'] || 'left';
|
||||
|
||||
// 字体大小和fontface
|
||||
ctx.font = style['font-family']
|
||||
? `${fontWeight} ${style['font-size']} "${style['font-family']}"`
|
||||
: `${fontWeight} ${style['font-size']} "Sans"`;
|
||||
// 文字对齐方式
|
||||
if (style['writing-mode'] === 'vertical-rl') {
|
||||
// 竖排文字
|
||||
ctx.fillTextVertical(
|
||||
view.text,
|
||||
x,
|
||||
y,
|
||||
parseInt(style['max-height']),
|
||||
parseInt(style['line-height']),
|
||||
view
|
||||
);
|
||||
} else {
|
||||
// 横排文字
|
||||
|
||||
// 是否旋转
|
||||
if (style['transform']) {
|
||||
ctx.translate(x + maxWidth / 2, y + lineHeight / 2);
|
||||
const rotateDeg = _rotateDeg(style['transform']);
|
||||
x = -maxWidth / 2;
|
||||
y = -lineHeight / 2;
|
||||
ctx.rotate(rotateDeg);
|
||||
}
|
||||
|
||||
ctx.wrapText(
|
||||
view.text,
|
||||
x,
|
||||
y,
|
||||
parseInt(style['max-width']),
|
||||
parseInt(style['line-height']),
|
||||
view
|
||||
);
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function _drawRect(ctx, view) {
|
||||
const style = view.style;
|
||||
const w = parseInt(style.width);
|
||||
const h = parseInt(style.height);
|
||||
let x = parseInt(style.left);
|
||||
let y = parseInt(style.top);
|
||||
const hasBd = style['border'] ? true : false;
|
||||
let bdr = style['border-radius'] || 0;
|
||||
|
||||
ctx.save();
|
||||
|
||||
// 圆角剪切
|
||||
if (bdr && bdr.endsWith('px')) {
|
||||
bdr = parseInt(bdr);
|
||||
} else if (bdr && bdr.endsWith('%')) {
|
||||
bdr = (w * parseInt(bdr)) / 100;
|
||||
}
|
||||
|
||||
// 是否旋转
|
||||
if (style['transform']) {
|
||||
ctx.translate(x + w / 2, y + h / 2);
|
||||
const rotateDeg = _rotateDeg(style['transform']);
|
||||
x = -w / 2;
|
||||
y = -h / 2;
|
||||
ctx.rotate(rotateDeg);
|
||||
}
|
||||
|
||||
if (hasBd) {
|
||||
const borderInfo = style['border'].split(' ');
|
||||
ctx.lineWidth = parseInt(borderInfo[0]);
|
||||
ctx.strokeStyle = borderInfo[2];
|
||||
}
|
||||
ctx.fillStyle = style['back-ground'];
|
||||
|
||||
ctx.drawRoundRect(x, y, w, h, bdr, true, hasBd);
|
||||
drawShadow(ctx, style);
|
||||
ctx.fill();
|
||||
ctx.clip();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function _absorbImgs(imgs) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const imgsNum = imgs.length;
|
||||
const result = {};
|
||||
try {
|
||||
for (let i = 0; i < imgsNum; i++) {
|
||||
const imgInfo = imgs[i];
|
||||
const imgData = await loadImage(imgInfo.url);
|
||||
result[imgInfo.index] = imgData;
|
||||
}
|
||||
resolve(result);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 提取旋转角度
|
||||
function _rotateDeg(str) {
|
||||
const reg = /^rotate\((-?\d*)deg\)$/;
|
||||
const result = reg.exec(str)[1];
|
||||
return (parseInt(result) * Math.PI) / 180 || 0;
|
||||
}
|
13
src/utils/painter/round_path.js
Normal file
13
src/utils/painter/round_path.js
Normal file
@ -0,0 +1,13 @@
|
||||
module.exports = function roundPath(x, y, w, h, r) {
|
||||
var min_size = Math.min(w, h);
|
||||
if (r > min_size / 2) r = min_size / 2;
|
||||
// 开始绘制
|
||||
this.beginPath();
|
||||
this.moveTo(x + r, y);
|
||||
this.arcTo(x + w, y, x + w, y + h, r);
|
||||
this.arcTo(x + w, y + h, x, y + h, r);
|
||||
this.arcTo(x, y + h, x, y, r);
|
||||
this.arcTo(x, y, x + w, y, r);
|
||||
this.closePath();
|
||||
return this;
|
||||
};
|
16
src/utils/painter/round_rect.js
Normal file
16
src/utils/painter/round_rect.js
Normal file
@ -0,0 +1,16 @@
|
||||
module.exports = function drawRoundRect(x, y, width, height, r, fill, stroke) {
|
||||
this.save();
|
||||
this.beginPath(); // draw top and top right corner
|
||||
this.moveTo(x + r, y);
|
||||
this.arcTo(x + width, y, x + width, y + r, r); // draw right side and bottom right corner
|
||||
this.arcTo(x + width, y + height, x + width - r, y + height, r); // draw bottom and bottom left corner
|
||||
this.arcTo(x, y + height, x, y + height - r, r); // draw left and top left corner
|
||||
this.arcTo(x, y, x + r, y, r);
|
||||
if (fill) {
|
||||
this.fill();
|
||||
}
|
||||
if (stroke) {
|
||||
this.stroke();
|
||||
}
|
||||
this.restore();
|
||||
};
|
22
src/utils/painter/shadow.js
Normal file
22
src/utils/painter/shadow.js
Normal file
@ -0,0 +1,22 @@
|
||||
// shadow 支持 (x, y, blur, color), 不支持 spread
|
||||
// shadow:0px 0px 10px rgba(0,0,0,0.1);
|
||||
module.exports = function drawShadow(ctx, style) {
|
||||
if (!style || (!style['box-shadow'] && !style['text-shadow'])) {
|
||||
return;
|
||||
}
|
||||
let box;
|
||||
if (style['text-shadow']) {
|
||||
box = style['text-shadow'].replace(/,\s+/g, ',').split(' ');
|
||||
} else {
|
||||
box = style['box-shadow'].replace(/,\s+/g, ',').split(' ');
|
||||
}
|
||||
|
||||
if (box.length > 4) {
|
||||
console.error("shadow don't spread option");
|
||||
return;
|
||||
}
|
||||
ctx.shadowOffsetX = parseInt(box[0], 10);
|
||||
ctx.shadowOffsetY = parseInt(box[1], 10);
|
||||
ctx.shadowBlur = parseInt(box[2], 10);
|
||||
ctx.shadowColor = box[3];
|
||||
};
|
7
src/utils/painter/transfer_base64.js
Normal file
7
src/utils/painter/transfer_base64.js
Normal file
@ -0,0 +1,7 @@
|
||||
const fs = require('fs');
|
||||
|
||||
module.exports = function transformBase64(data, path) {
|
||||
const base64 = data.replace(/^data:image\/\w+;base64,/, '');
|
||||
const dataBuffer = new Buffer(base64, 'base64');
|
||||
fs.writeFileSync(path, dataBuffer);
|
||||
};
|
106
src/utils/painter/vertical_text.js
Normal file
106
src/utils/painter/vertical_text.js
Normal file
@ -0,0 +1,106 @@
|
||||
/**
|
||||
* @author zhangxinxu(.com)
|
||||
* @licence MIT
|
||||
* @description http://www.zhangxinxu.com/wordpress/?p=7362
|
||||
*/
|
||||
|
||||
var drawShadow = require('./shadow');
|
||||
|
||||
module.exports = function fillTextVertical(
|
||||
text,
|
||||
x,
|
||||
y,
|
||||
maxHeight,
|
||||
lineHeight,
|
||||
view
|
||||
) {
|
||||
var context = this;
|
||||
var canvas = context.canvas;
|
||||
|
||||
var arrText = text.split('');
|
||||
var arrWidth = arrText.map(function(letter) {
|
||||
return context.measureText(letter).width;
|
||||
});
|
||||
|
||||
var align = context.textAlign;
|
||||
var baseline = context.textBaseline;
|
||||
|
||||
var style = view.style;
|
||||
var hasStroke = style['-webkit-text-stroke'] ? true : false;
|
||||
var strokeInfo = [];
|
||||
|
||||
var fontSize = parseInt(style['font-size']);
|
||||
|
||||
if (hasStroke) {
|
||||
strokeInfo = style['-webkit-text-stroke'].split(' ');
|
||||
context.lineWidth = parseInt(strokeInfo[0]);
|
||||
context.strokeStyle = strokeInfo[1];
|
||||
}
|
||||
|
||||
// if (align == 'left') {
|
||||
// x = x + Math.max.apply(null, arrWidth) / 2;
|
||||
// } else if (align == 'right') {
|
||||
// x = x - Math.max.apply(null, arrWidth) / 2;
|
||||
// }
|
||||
|
||||
if (
|
||||
baseline == 'bottom' ||
|
||||
baseline == 'alphabetic' ||
|
||||
baseline == 'ideographic'
|
||||
) {
|
||||
y = y - arrWidth[0] / 2;
|
||||
} else if (baseline == 'top' || baseline == 'hanging') {
|
||||
y = y + arrWidth[0] / 2;
|
||||
}
|
||||
|
||||
context.textAlign = 'left';
|
||||
context.textBaseline = 'top';
|
||||
|
||||
// 开始逐字绘制
|
||||
var curHeihgt = 0;
|
||||
var initialY = y;
|
||||
|
||||
arrText.forEach(function(letter, index) {
|
||||
// 确定下一个字符的纵坐标位置
|
||||
|
||||
var letterWidth = arrWidth[index];
|
||||
curHeihgt += letterWidth;
|
||||
// 换行
|
||||
if (curHeihgt > maxHeight || letter === '\n') {
|
||||
x -= lineHeight;
|
||||
y = initialY;
|
||||
curHeihgt = 0;
|
||||
}
|
||||
// 是否需要旋转判断
|
||||
var code = letter.charCodeAt(0);
|
||||
if (code <= 256 || code == 8212) {
|
||||
context.translate(x + fontSize / 2, y + fontSize / 2);
|
||||
// 英文字符,旋转90°
|
||||
context.rotate((90 * Math.PI) / 180);
|
||||
context.translate(-x - fontSize / 2, -y - fontSize / 2);
|
||||
} else if (index > 0 && text.charCodeAt(index - 1) < 256) {
|
||||
// y修正
|
||||
y = y + arrWidth[index - 1] / 2;
|
||||
}
|
||||
|
||||
// 绘制阴影
|
||||
if (style['text-shadow']) {
|
||||
drawShadow(context, view.style);
|
||||
}
|
||||
// 描边
|
||||
if (hasStroke) {
|
||||
context.strokeText(letter, x, y);
|
||||
}
|
||||
|
||||
context.fillText(letter, x, y);
|
||||
|
||||
// 旋转坐标系还原成初始态
|
||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
// 确定下一个字符的纵坐标位置
|
||||
var letterWidth = arrWidth[index];
|
||||
y = y + letterWidth;
|
||||
});
|
||||
// 水平垂直对齐方式还原
|
||||
context.textAlign = align;
|
||||
context.textBaseline = baseline;
|
||||
};
|
71
src/utils/painter/wrap_text.js
Normal file
71
src/utils/painter/wrap_text.js
Normal file
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @author zhangxinxu(.com)
|
||||
* @licence MIT
|
||||
* @description http://www.zhangxinxu.com/wordpress/?p=7362
|
||||
*/
|
||||
|
||||
var drawShadow = require('./shadow');
|
||||
|
||||
module.exports = function wrapText(text, x, y, maxWidth, lineHeight, view) {
|
||||
if (
|
||||
typeof text != 'string' ||
|
||||
typeof x != 'number' ||
|
||||
typeof y != 'number' ||
|
||||
typeof maxWidth != 'number' ||
|
||||
typeof lineHeight != 'number'
|
||||
) {
|
||||
console.log('必填参数有误', text, x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
var context = this;
|
||||
var canvas = context.canvas;
|
||||
|
||||
// 字符分隔为数组
|
||||
var arrText = text.split('');
|
||||
var line = '';
|
||||
|
||||
var style = view.style;
|
||||
var hasStroke = style['-webkit-text-stroke'] ? true : false;
|
||||
var strokeInfo = [];
|
||||
|
||||
context.textBaseline = 'top';
|
||||
|
||||
if (hasStroke) {
|
||||
strokeInfo = style['-webkit-text-stroke'].split(' ');
|
||||
context.lineWidth = parseInt(strokeInfo[0]);
|
||||
context.strokeStyle = strokeInfo[1];
|
||||
}
|
||||
|
||||
for (var n = 0; n < arrText.length; n++) {
|
||||
var testLine = line + arrText[n];
|
||||
var metrics = context.measureText(testLine);
|
||||
var testWidth = metrics.width;
|
||||
|
||||
if ((testWidth > maxWidth && n > 0) || arrText[n] === '\n') {
|
||||
// 绘制阴影
|
||||
if (style['text-shadow']) {
|
||||
drawShadow(context, view.style);
|
||||
}
|
||||
// 描边
|
||||
if (hasStroke) {
|
||||
context.strokeText(line, x, y);
|
||||
}
|
||||
context.fillText(line, x, y);
|
||||
line = arrText[n] === '\n' ? '' : arrText[n];
|
||||
y += lineHeight;
|
||||
} else {
|
||||
line = testLine;
|
||||
}
|
||||
}
|
||||
// 绘制阴影
|
||||
if (style['text-shadow']) {
|
||||
drawShadow(context, view.style);
|
||||
}
|
||||
// 描边
|
||||
if (hasStroke) {
|
||||
context.strokeText(line, x, y);
|
||||
}
|
||||
|
||||
context.fillText(line, x, y);
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user