commit message

This commit is contained in:
Chopper
2021-05-13 11:03:32 +08:00
commit 23804939eb
2158 changed files with 149684 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
import { DrawPosterUseOpts } from '../../utils/interface';
export interface CreateLayerOpts {
background?: string;
self?: boolean;
line?: boolean;
lineHeight?: number;
}
export interface DrawRowOpt {
text?: string;
font?: string;
color?: string;
center?: boolean;
width?: number;
}
declare const _default: DrawPosterUseOpts;
/** 绘制表单扩展方法 */
export default _default;

View File

@@ -0,0 +1,140 @@
/** 绘制表单扩展方法 */
export default {
name: 'createLayer',
init: (dp) => {
dp.from = {
height: 0,
padding: 8,
margin: 0
};
dp.setFromOptions = (opts) => {
if (typeof opts.height !== 'undefined') {
dp.from.height = opts.height;
}
if (typeof opts.margin !== 'undefined') {
dp.from.margin = opts.margin;
}
if (typeof opts.padding !== 'undefined') {
dp.from.padding = opts.padding;
}
};
},
handle: (dp, afferOpts, rowList) => {
// 当前配置(头部偏移量, 列内边距, 表单外边距)
const height = dp.from.height;
const margin = dp.from.margin;
const padding = dp.from.padding;
// 当前层宽度
const containerWidth = dp.canvas.width - (margin * 2);
// 基本层配置
const opts = Object.assign({ background: "#fff", columnY: height || margin, self: true, line: true, lineHeight: 0, border: true }, afferOpts);
// 基本列配置
const baseRowOpts = {
text: "",
font: "24px sans-serif",
color: "#333",
center: false,
width: 0,
};
// 累计最高的列为标准定义为层高度
let maxRowHeight = 0;
// 累计固定栅格列偏移量
let columnOffsetX = margin;
// 创建行绘制任务
const drawLayerInfos = rowList.map((afferRowOpts = {}, index) => {
const rowOpts = Object.assign(Object.assign({}, baseRowOpts), afferRowOpts);
let columnX = 0; // 每列的X轴
let columnW = 0; // 每列的宽度
let fontOffsetX = 0; // 字体偏移X轴
let fontMaxWidth = 100; // 字体最大宽度
opts.lineHeight = opts.lineHeight || Number(rowOpts.font.replace(/[^0-9.]/g, ""));
if (opts.self) {
// 自适应栅格格子计算
columnX = containerWidth - (containerWidth / (index + 1)) + margin;
columnW = containerWidth / rowList.length;
if (columnX > 0 && columnX < containerWidth - columnW) {
columnX = (columnW * index) + margin;
}
fontOffsetX = rowOpts.center ? columnX + (columnW / 2) : columnX + padding;
fontMaxWidth = columnW - (padding * 3);
}
if (!opts.self) {
// 固定栅格格子计算
columnW = rowOpts.width;
columnX = columnOffsetX;
fontMaxWidth = columnW - (padding * 3);
fontOffsetX = rowOpts.center ? columnOffsetX + (rowOpts.width / 2) : columnOffsetX + padding;
columnOffsetX += rowOpts.width;
}
dp.ctx.font = rowOpts.font;
const drawFontInfos = dp.ctx.fillWarpText({
text: rowOpts.text,
maxWidth: fontMaxWidth,
lineHeight: opts.lineHeight,
x: fontOffsetX,
y: opts.columnY,
layer: 10,
notFillText: true
});
// 当前行的高度
const rowHeight = (opts.lineHeight * drawFontInfos.length) + (padding * 3);
// 若该列高度大于累计高度, 将累计高度替换
if (rowHeight > maxRowHeight) {
maxRowHeight = rowHeight;
}
return {
font: rowOpts.font,
center: rowOpts.center,
color: rowOpts.color,
border: opts.border,
background: opts.background,
lineHeight: opts.lineHeight,
line: opts.line,
drawFontInfos,
columnY: opts.columnY,
columnX,
columnW,
columnH: maxRowHeight,
margin,
padding
};
});
// 将行绘制任务添加至绘制容器中
dp.draw((ctx) => drawLayerInfos.forEach((rowOpts, index) => {
ctx.font = rowOpts.font;
ctx.fillStyle = rowOpts.background;
ctx.strokeStyle = "#333";
ctx.textBaseline = "middle";
ctx.textAlign = 'left';
if (rowOpts.center) {
ctx.textAlign = "center";
}
ctx.fillRect(rowOpts.columnX, rowOpts.columnY, rowOpts.columnW, rowOpts.columnH);
if (rowOpts.border) {
dp.ctx.strokeRect(margin, rowOpts.columnY, dp.canvas.width - margin, maxRowHeight);
}
if (rowOpts.line && rowOpts.columnX !== margin) {
ctx.moveTo(rowOpts.columnX, rowOpts.columnY);
ctx.lineTo(rowOpts.columnX, rowOpts.columnY + rowOpts.columnH);
ctx.stroke();
ctx.beginPath();
}
ctx.fillStyle = rowOpts.color;
rowOpts.drawFontInfos.forEach(fontInfo => {
// 计算每行字体绘制y轴长度
// y(当前列置顶轴) + (rowOpts.columnH(当前列最高长度) / 2) - (((总列数-1) * 行高) / 2)
const textTotal = rowOpts.drawFontInfos.length - 1;
const textMiddleY = (textTotal * rowOpts.lineHeight) / 2;
let fontOffsetY = fontInfo.y + (rowOpts.columnH / 2);
fontOffsetY -= textMiddleY;
ctx.fillText(fontInfo.text, fontInfo.x, fontOffsetY);
});
}));
if (opts.columnY === 0 || opts.columnY === margin) {
maxRowHeight += margin;
}
// 叠加高度
dp.from.height += maxRowHeight;
return maxRowHeight;
},
};

View File

@@ -0,0 +1,4 @@
import { DrawPosterUseOpts } from '../../utils/interface';
export * from './gcanvas';
declare const _default: DrawPosterUseOpts;
export default _default;

View File

@@ -0,0 +1,9 @@
import { WeexBridge, enable, Image } from './gcanvas';
export * from './gcanvas';
import DrawPoster from "../../draw-poster";
DrawPoster.prototype['gcanvas'] = {
WeexBridge,
enable,
Image
};
export default {};

View File

@@ -0,0 +1,12 @@
import { DrawPosterUseCtxOpts } from '../../utils/interface';
import { ObjectFit, ObjectPosition, Size } from "../../utils/object-sizing";
export interface ImageFitOption {
radius?: number;
objectFit?: ObjectFit;
intrinsicSize?: Size;
specifiedSize?: Size;
intrinsicPosition?: ObjectPosition;
specifiedPosition?: [number, number];
}
declare const _default: DrawPosterUseCtxOpts;
export default _default;

View File

@@ -0,0 +1,25 @@
import { calculateConcreteRect } from "../../utils/object-sizing";
import uni from "../../utils/global";
export default {
name: 'drawImageFit',
handle: async (canvas, ctx, url, options) => {
var _a, _b, _c;
const [error, imageInfo] = await uni.getImageInfo({ src: url });
// 配置默认值
const style = Object.assign({ radius: 0, objectFit: 'cover', intrinsicSize: { width: (_a = imageInfo === null || imageInfo === void 0 ? void 0 : imageInfo.width) !== null && _a !== void 0 ? _a : 100, height: (_b = imageInfo === null || imageInfo === void 0 ? void 0 : imageInfo.height) !== null && _b !== void 0 ? _b : 100 }, specifiedSize: { width: 100, height: 100 }, intrinsicPosition: ['center', 'center'], specifiedPosition: [0, 0] }, options);
// 计算图片尺寸
const drawImageInfo = calculateConcreteRect(style, style.intrinsicSize, style.specifiedSize);
// 如有圆角, 则进行裁剪
if (style.radius > 0) {
ctx.save();
(_c = ctx.setFillStyle) === null || _c === void 0 ? void 0 : _c.call(ctx, 'transparent');
ctx.fillStyle = 'transparent';
ctx.fillRoundRect(style.specifiedPosition[0], style.specifiedPosition[1], style.specifiedSize.width, style.specifiedSize.height, style.radius);
ctx.clip();
}
const result = await ctx.drawImage(url, ...Object.values(drawImageInfo));
if (style.radius > 0)
ctx.restore();
return result;
}
};

View File

@@ -0,0 +1,4 @@
import { DrawPosterUseCtxOpts } from '../../utils/interface';
declare const _default: DrawPosterUseCtxOpts;
/** 等待绘制图片原型方法 */
export default _default;

View File

@@ -0,0 +1,42 @@
import { downloadImgUrl } from '../../utils/wx-utils';
/** 等待绘制图片原型方法 */
export default {
name: 'drawImage',
init: (canvas, ctx) => {
ctx.drawImageProto = ctx.drawImage;
},
handle: async (canvas, ctx, url, sx, sy, sh, sw, dx, dy, dh, dw) => {
// 下载路径
const path = await downloadImgUrl(url);
// 标记当前绘画存在图片绘制
let result = false;
// 基本绘制方法, 如果是 fit 方式, 则传入所有参数, 不然则只传入四个参数
const baseDrawImage = (imageResource) => {
const isFit = typeof dx === 'number' && typeof dw === 'number';
if (isFit) {
ctx.drawImageProto(imageResource, sx, sy, sh, sw, dx, dy, dh, dw);
}
else {
ctx.drawImageProto(imageResource, sx, sy, sh, sw);
}
};
// 如果是 context 绘制方式, 则直接绘制
if (ctx.drawType === 'context') {
baseDrawImage(path);
result = true;
}
// 如果是 type2d 绘制方式, 则等待图片绘制完毕
if (ctx.drawType === 'type2d') {
result = await new Promise(resolve => {
const image = canvas.createImage();
image.src = path;
image.onload = () => {
baseDrawImage(image);
resolve(true);
};
image.onerror = () => resolve(false);
});
}
return result;
}
};

View File

@@ -0,0 +1,4 @@
import { DrawPosterUseCtxOpts } from '../../utils/interface';
declare const _default: DrawPosterUseCtxOpts;
/** 绘制圆角图片原型方法 */
export default _default;

View File

@@ -0,0 +1,15 @@
/** 绘制圆角图片原型方法 */
export default {
name: 'drawRoundImage',
handle: async (canvas, ctx, url, x, y, w, h, r = 15) => {
var _a;
ctx.save();
(_a = ctx.setFillStyle) === null || _a === void 0 ? void 0 : _a.call(ctx, 'transparent');
ctx.fillStyle = 'transparent';
ctx.fillRoundRect(x, y, w, h, r);
ctx.clip();
const result = await ctx.drawImage(url, x, y, w, h);
ctx.restore();
return result;
}
};

View File

@@ -0,0 +1,4 @@
import { DrawPosterUseCtxOpts } from '../../utils/interface';
declare const _default: DrawPosterUseCtxOpts;
/** 绘制填充圆角矩形方法 */
export default _default;

View File

@@ -0,0 +1,7 @@
/** 绘制填充圆角矩形方法 */
export default {
name: 'fillRoundRect',
handle: (canvas, ctx, x, y, w, h, r) => {
ctx.roundRect(x, y, w, h, r, true);
}
};

View File

@@ -0,0 +1,4 @@
import { DrawPosterUseCtxOpts } from '../../utils/interface';
declare const _default: DrawPosterUseCtxOpts;
/** 绘制换行字体原型方法 */
export default _default;

View File

@@ -0,0 +1,76 @@
/** 绘制换行字体原型方法 */
export default {
name: 'fillWarpText',
handle: (canvas, ctx, config) => {
const newConfig = config = Object.assign({ maxWidth: 100, layer: 2, lineHeight: Number(ctx.font.replace(/[^0-9.]/g, '')), x: 0, y: Number(ctx.font.replace(/[^0-9.]/g, '')) / 1.2, splitText: '', notFillText: false }, config);
const { text, splitText, maxWidth, layer, lineHeight, notFillText, x, y } = newConfig;
// 当字符串为空时, 抛出错误
if (!text) {
throw Error('warpFillText Error: text is empty string');
}
// 分割所有单个字符串
const chr = text.split(splitText);
// 存入的每行字体的容器
let row = [];
// 判断字符串
let timp = '';
if (splitText) {
row = chr;
}
else {
// 遍历所有字符串, 填充行容器
for (let i = 0; i < chr.length; i++) {
// 当超出行列时, 停止执行遍历, 节省计算时间
if (row.length > layer) {
break;
}
if (ctx.measureText(timp).width < maxWidth) {
// 如果超出长度, 添加进row数组
timp += chr[i];
}
else {
// 如超出一行长度, 则换行, 并清除容器
i--;
row.push(timp);
timp = '';
}
}
// 如有剩下字体, 则在最后时添加一行
if (timp) {
row.push(timp);
}
// 如果数组长度大于指定行数
if (row.length > layer) {
row = row.slice(0, layer);
// 结束的索引
const end = layer - 1;
for (let i = 0; i < row[end].length; i++) {
const currentWidth = ctx.measureText(`${row[end]}...`).width;
if (currentWidth > maxWidth) {
// 加上... 当前宽度大于最大宽度时, 去除一位字符串
const strEnd = row[end].length - 1;
row[end] = row[end].slice(0, strEnd);
}
else {
row[end] += '...';
break;
}
}
}
}
// 储存并返回绘制信息
const drawInfos = row.map((item, index) => {
const info = {
text: item,
y: y + index * lineHeight,
x: x,
};
// 默认执行绘制信息
if (!notFillText) {
ctx.fillText(info.text, info.x, info.y);
}
return info;
});
return drawInfos;
}
};

View File

@@ -0,0 +1,7 @@
export { default as drawImage } from "./draw-image";
export { default as roundRect } from "./round-rect";
export { default as fillRoundRect } from "./fill-round-rect";
export { default as strokeRoundRect } from "./stroke-round-rect";
export { default as fillWarpText } from "./fill-warp-text";
export { default as drawRoundImage } from "./draw-round-image";
export { default as drawImageFit } from "./draw-image-fit";

View File

@@ -0,0 +1,15 @@
/*
* @Author: Mr.Mao
* @LastEditors: Mr.Mao
* @Date: 2020-11-11 20:43:33
* @LastEditTime: 2021-01-02 00:16:59
* @Description:
* @任何一个傻子都能写出让电脑能懂的代码,而只有好的程序员可以写出让人能看懂的代码
*/
export { default as drawImage } from "./draw-image";
export { default as roundRect } from "./round-rect";
export { default as fillRoundRect } from "./fill-round-rect";
export { default as strokeRoundRect } from "./stroke-round-rect";
export { default as fillWarpText } from "./fill-warp-text";
export { default as drawRoundImage } from "./draw-round-image";
export { default as drawImageFit } from "./draw-image-fit";

View File

@@ -0,0 +1,4 @@
import { DrawPosterUseCtxOpts } from '../../utils/interface';
declare const _default: DrawPosterUseCtxOpts;
/** 绘制圆角矩形原型方法 */
export default _default;

View File

@@ -0,0 +1,41 @@
/** 绘制圆角矩形原型方法 */
export default {
name: 'roundRect',
handle: (canvas, ctx, x, y, w, h, r = 15, fill = false, stroke = false) => {
if (r === 0) {
if (stroke)
ctx.strokeRect(x, y, w, h);
if (fill)
ctx.fillRect(x, y, w, h);
return;
}
if (w < 2 * r) {
r = w / 2;
}
if (h < 2 * r) {
r = h / 2;
}
// 开始绘制
ctx.beginPath();
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
// 移动复制
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.lineTo(x + w, y + r);
// (x,y,z,j,f) x,y圆心z半径,j起始弧度f终止弧度
ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
ctx.lineTo(x + w, y + h - r);
ctx.lineTo(x + w - r, y + h);
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
ctx.lineTo(x + r, y + h);
ctx.lineTo(x, y + h - r);
ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
ctx.lineTo(x, y + r);
ctx.lineTo(x + r, y);
if (stroke)
ctx.stroke();
if (fill)
ctx.fill();
ctx.closePath();
}
};

View File

@@ -0,0 +1,4 @@
import { DrawPosterUseCtxOpts } from '../../utils/interface';
declare const _default: DrawPosterUseCtxOpts;
/** 绘制填充圆角矩形方法 */
export default _default;

View File

@@ -0,0 +1,7 @@
/** 绘制填充圆角矩形方法 */
export default {
name: 'strokeRoundRect',
handle: (canvas, ctx, x, y, w, h, r) => {
ctx.roundRect(x, y, w, h, r, false, true);
}
};

View File

@@ -0,0 +1,101 @@
import { DrawPosterUseOpts } from '../../utils/interface';
import { ImageFitOption } from '../draw-function/draw-image-fit';
/** 矩形基本信息 */
interface PainterItemSize {
/** 容器的宽度,固定值 */
width: number;
/** 容器的高度,固定值 */
height: number;
}
/** 元素位置信息 */
interface PainterItemSite {
/** 元素锚点距左边的距离; 默认: 0 */
left?: number;
/** 元素锚点距上边的距离; 默认: 0 */
top?: number;
}
/** 绘制图片信息 */
interface PainterImageInfo extends PainterItemSize, PainterItemSite {
/** 绘制图片元素 */
type: 'image';
/** 图片地址 */
src: string;
/** 图片自适应, 可参考 css 属性 object-fit */
objectFit?: ImageFitOption['objectFit'];
/** 图片在元素容器中显示的位置,可参考 css 属性 object-position */
position?: ImageFitOption['intrinsicPosition'];
/** 圆角尺寸; 默认: 0 */
radius?: number;
}
/** 绘制矩形信息 */
interface PainterRectInfo extends PainterItemSize, PainterItemSite {
/** 绘制矩形元素 */
type: "rect";
/** 矩形背景颜色; 默认: "#000" */
background?: string;
/** 圆角尺寸; 默认: 0 */
radius?: number;
}
/** 绘制单行文字信息 */
interface PainterTextInfo extends PainterItemSite {
/** 绘制文本元素 */
type: "text";
/** 文本颜色; 默认: "#000" */
color?: string;
/** 字体; 默认: "serial" */
fontFamily?: string;
/** 字号(单位rpx); 默认: 30 rpx */
fontSize?: number;
/** 字重; 默认: "normal" 可选项: "bold" */
fontWeight?: string;
/** 字型 默认: "normal" 可选项: "italic" */
fontStyle?: string;
/** 元素的宽度(单位rpx), 水平排布时影响后一个元素的位置,为 null 时根据文字实际占用的宽度计算 */
width?: number;
/** 文本内容 */
content: string;
}
/** 绘制多行文字信息 */
interface PainterLineFeedTextInfo extends PainterItemSite {
/** 绘制换行文本元素 */
type: "line-feed-text";
/** 文本颜色; 默认: "#000" */
color?: string;
/** 字体; 默认: "serial" */
fontFamily?: string;
/** 字号(单位rpx); 默认: 30 rpx */
fontSize?: number;
/** 字重; 默认: "normal" 可选项: "bold" */
fontWeight?: string;
/** 字型 默认: "normal" 可选项: "italic" */
fontStyle?: string;
/** 文本块的宽度,不能为空 */
width: number;
/** 行高; 默认取当前文字行高 */
lineHeight?: number;
/** 文本最大行数,超出即显示省略号; 默认3行 */
lineClamp?: number;
/** 文本内容 */
content: string;
}
/** 绘制二维码信息 */
interface PainterQrCodeInfo extends PainterItemSite {
/** 绘制换行文本元素 */
type: "qr-code";
/** 二维码尺寸 */
size: number;
/** 二维码内容 */
content: string;
/** 边距二维码实际尺寸会根据所设边距值进行缩放调整默认5 */
margin?: number;
/** 背景色(默认:'#ffffff'*/
backgroundColor?: string;
/** 前景色(默认:'#000000' */
foregroundColor?: string;
}
export interface PainterContainerOption extends PainterItemSize {
/** 绘制项的数组 */
contents: Array<PainterImageInfo | PainterRectInfo | PainterTextInfo | PainterLineFeedTextInfo | PainterQrCodeInfo>;
}
declare const _default: DrawPosterUseOpts;
export default _default;

View File

@@ -0,0 +1,73 @@
export default {
name: 'painter',
handle: (dp, option) => {
dp.canvas.width = option.width;
dp.canvas.height = option.height;
dp.draw(async (ctx) => {
for (let i = 0; i < option.contents.length; i++) {
ctx.save();
const drawInfo = option.contents[i];
const { left = 0, top = 0 } = drawInfo;
if (drawInfo.type === 'rect') {
ctx.fillStyle = drawInfo.background || '#000000';
ctx.fillRoundRect(left, top, drawInfo.width, drawInfo.height, drawInfo.radius || 0);
}
if (drawInfo.type === 'image') {
await ctx.drawImageFit(drawInfo.src, {
objectFit: drawInfo.objectFit || 'cover',
intrinsicPosition: drawInfo.position || ['center', 'center'],
specifiedPosition: [left, top],
specifiedSize: {
width: drawInfo.width,
height: drawInfo.height
},
radius: drawInfo.radius
});
}
if (drawInfo.type === 'text') {
ctx.fillStyle = drawInfo.color || '#000000';
ctx.font = `\
${drawInfo.fontStyle || 'normal'} \
${drawInfo.fontWeight || 'normal'} \
${drawInfo.fontSize || 30} \
${drawInfo.fontFamily || 'serial'}\
`;
ctx.fillText(drawInfo.content, left, top, drawInfo.width);
}
if (drawInfo.type === 'line-feed-text') {
ctx.fillStyle = drawInfo.color || '#000000';
ctx.font = `\
${drawInfo.fontStyle || 'normal'} \
${drawInfo.fontWeight || 'normal'} \
${drawInfo.fontSize || 30} \
${drawInfo.fontFamily || 'serial'}\
`;
ctx.fillWarpText({
x: drawInfo.left,
y: drawInfo.top,
layer: drawInfo.lineClamp,
lineHeight: drawInfo.lineHeight,
maxWidth: drawInfo.width,
text: drawInfo.content
});
}
if (drawInfo.type === 'qr-code') {
if (typeof ctx.drawQrCode !== 'function') {
console.error('--- 当前未引入qr-code扩展, 将自动省略该二维码绘制 ---');
return false;
}
ctx.drawQrCode({
x: left,
y: top,
size: drawInfo.size,
text: drawInfo.content,
margin: drawInfo.margin || 5,
backgroundColor: drawInfo.backgroundColor || '#ffffff',
foregroundColor: drawInfo.foregroundColor || '#000000',
});
}
ctx.restore();
}
});
}
};

View File

@@ -0,0 +1,6 @@
declare const _default: {
name: string;
handle: any;
errorCorrectLevel: any;
};
export default _default;

View File

@@ -0,0 +1,6 @@
import uQRCode from "./uQRCode";
export default {
name: "drawQrCode",
handle: uQRCode.make.bind(uQRCode),
errorCorrectLevel: uQRCode.errorCorrectLevel
};

View File

@@ -0,0 +1,10 @@
/*
* @Author: Mr.Mao
* @LastEditors: Mr.Mao
* @Date: 2021-01-02 13:30:58
* @LastEditTime: 2021-01-02 13:31:27
* @Description:
* @任何一个傻子都能写出让电脑能懂的代码,而只有好的程序员可以写出让人能看懂的代码
*/
declare const uQRCode: any
export default uQRCode

File diff suppressed because it is too large Load Diff