const GD = require('./gradient.js');
export default class Painter {
constructor(ctx, data) {
this.ctx = ctx;
this.data = data;
this.globalWidth = {};
this.globalHeight = {};
}
isMoving = false
movingCache = {}
paint(callback, isMoving, movingCache) {
this.style = {
width: this.data.width.toPx(),
height: this.data.height.toPx(),
};
if (isMoving) {
this.isMoving = true
this.movingCache = movingCache
}
this._background();
for (const view of this.data.views) {
this._drawAbsolute(view);
}
this.ctx.draw(false, () => {
callback && callback(this.callbackInfo);
});
}
_background() {
this.ctx.save();
const {
width,
height,
} = this.style;
const bg = this.data.background;
this.ctx.translate(width / 2, height / 2);
this._doClip(this.data.borderRadius, width, height);
if (!bg) {
// 如果未设置背景,则默认使用透明色
this.ctx.fillStyle = 'transparent';
this.ctx.fillRect(-(width / 2), -(height / 2), width, height);
} else if (bg.startsWith('#') || bg.startsWith('rgba') || bg.toLowerCase() === 'transparent') {
// 背景填充颜色
this.ctx.fillStyle = bg;
this.ctx.fillRect(-(width / 2), -(height / 2), width, height);
} else if (GD.api.isGradient(bg)) {
GD.api.doGradient(bg, width, height, this.ctx);
this.ctx.fillRect(-(width / 2), -(height / 2), width, height);
} else {
// 背景填充图片
this.ctx.drawImage(bg, -(width / 2), -(height / 2), width, height);
}
this.ctx.restore();
}
_drawAbsolute(view) {
if (!(view && view.type)) {
// 过滤无效 view
return
}
// 证明 css 为数组形式,需要合并
if (view.css && view.css.length) {
/* eslint-disable no-param-reassign */
view.css = Object.assign(...view.css);
}
switch (view.type) {
case 'image':
this._drawAbsImage(view);
break;
case 'text':
this._fillAbsText(view);
break;
case 'rect':
this._drawAbsRect(view);
break;
default:
break;
}
}
_border({
borderRadius = 0,
width,
height,
borderWidth = 0,
borderStyle = 'solid'
}) {
let r1 = 0,
r2 = 0,
r3 = 0,
r4 = 0
const minSize = Math.min(width, height);
if (borderRadius) {
const border = borderRadius.split(/\s+/)
if (border.length === 4) {
r1 = Math.min(border[0].toPx(false, minSize), width / 2, height / 2);
r2 = Math.min(border[1].toPx(false, minSize), width / 2, height / 2);
r3 = Math.min(border[2].toPx(false, minSize), width / 2, height / 2);
r4 = Math.min(border[3].toPx(false, minSize), width / 2, height / 2);
} else {
r1 = r2 = r3 = r4 = Math.min(borderRadius && borderRadius.toPx(false, minSize), width / 2, height / 2);
}
}
const lineWidth = borderWidth && borderWidth.toPx(false, minSize);
this.ctx.lineWidth = lineWidth;
if (borderStyle === 'dashed') {
this.ctx.setLineDash([lineWidth * 4 / 3, lineWidth * 4 / 3]);
// this.ctx.lineDashOffset = 2 * lineWidth
} else if (borderStyle === 'dotted') {
this.ctx.setLineDash([lineWidth, lineWidth]);
}
const notSolid = borderStyle !== 'solid'
this.ctx.beginPath();
notSolid && r1 === 0 && this.ctx.moveTo(-width / 2 - lineWidth, -height / 2 - lineWidth / 2) // 顶边虚线规避重叠规则
r1 !== 0 && this.ctx.arc(-width / 2 + r1, -height / 2 + r1, r1 + lineWidth / 2, 1 * Math.PI, 1.5 * Math.PI); //左上角圆弧
this.ctx.lineTo(r2 === 0 ? notSolid ? width / 2 : width / 2 + lineWidth / 2 : width / 2 - r2, -height / 2 - lineWidth / 2); // 顶边线
notSolid && r2 === 0 && this.ctx.moveTo(width / 2 + lineWidth / 2, -height / 2 - lineWidth) // 右边虚线规避重叠规则
r2 !== 0 && this.ctx.arc(width / 2 - r2, -height / 2 + r2, r2 + lineWidth / 2, 1.5 * Math.PI, 2 * Math.PI); // 右上角圆弧
this.ctx.lineTo(width / 2 + lineWidth / 2, r3 === 0 ? notSolid ? height / 2 : height / 2 + lineWidth / 2 : height / 2 - r3); // 右边线
notSolid && r3 === 0 && this.ctx.moveTo(width / 2 + lineWidth, height / 2 + lineWidth / 2) // 底边虚线规避重叠规则
r3 !== 0 && this.ctx.arc(width / 2 - r3, height / 2 - r3, r3 + lineWidth / 2, 0, 0.5 * Math.PI); // 右下角圆弧
this.ctx.lineTo(r4 === 0 ? notSolid ? -width / 2 : -width / 2 - lineWidth / 2 : -width / 2 + r4, height / 2 + lineWidth / 2); // 底边线
notSolid && r4 === 0 && this.ctx.moveTo(-width / 2 - lineWidth / 2, height / 2 + lineWidth) // 左边虚线规避重叠规则
r4 !== 0 && this.ctx.arc(-width / 2 + r4, height / 2 - r4, r4 + lineWidth / 2, 0.5 * Math.PI, 1 * Math.PI); // 左下角圆弧
this.ctx.lineTo(-width / 2 - lineWidth / 2, r1 === 0 ? notSolid ? -height / 2 : -height / 2 - lineWidth / 2 : -height / 2 + r1); // 左边线
notSolid && r1 === 0 && this.ctx.moveTo(-width / 2 - lineWidth, -height / 2 - lineWidth / 2) // 顶边虚线规避重叠规则
if (!notSolid) {
this.ctx.closePath();
}
}
/**
* 根据 borderRadius 进行裁减
*/
_doClip(borderRadius, width, height, borderStyle) {
if (borderRadius && width && height) {
// 防止在某些机型上周边有黑框现象,此处如果直接设置 fillStyle 为透明,在 Android 机型上会导致被裁减的图片也变为透明, iOS 和 IDE 上不会
// globalAlpha 在 1.9.90 起支持,低版本下无效,但把 fillStyle 设为了 white,相对默认的 black 要好点
this.ctx.globalAlpha = 0;
this.ctx.fillStyle = 'white';
this._border({
borderRadius,
width,
height,
borderStyle
})
this.ctx.fill();
// 在 ios 的 6.6.6 版本上 clip 有 bug,禁掉此类型上的 clip,也就意味着,在此版本微信的 ios 设备下无法使用 border 属性
if (!(getApp().systemInfo &&
getApp().systemInfo.version <= '6.6.6' &&
getApp().systemInfo.platform === 'ios')) {
this.ctx.clip();
}
this.ctx.globalAlpha = 1;
}
}
/**
* 画边框
*/
_doBorder(view, width, height) {
if (!view.css) {
return;
}
const {
borderRadius,
borderWidth,
borderColor,
borderStyle
} = view.css;
if (!borderWidth) {
return;
}
this.ctx.save();
this._preProcess(view, true);
this.ctx.strokeStyle = (borderColor || 'black');
this._border({
borderRadius,
width,
height,
borderWidth,
borderStyle
})
this.ctx.stroke();
this.ctx.restore();
}
_preProcess(view, notClip) {
let width = 0;
let height;
let extra;
const paddings = this._doPaddings(view);
switch (view.type) {
case 'text': {
const textArray = view.text.split('\n');
// 处理多个连续的'\n'
for (let i = 0; i < textArray.length; ++i) {
if (textArray[i] === '') {
textArray[i] = ' ';
}
}
const fontWeight = view.css.fontWeight === 'bold' ? 'bold' : 'normal';
const textStyle = view.css.textStyle === 'italic' ? 'italic' : 'normal';
if (!view.css.fontSize) {
view.css.fontSize = '20rpx';
}
this.ctx.font = `${textStyle} ${fontWeight} ${view.css.fontSize.toPx()}px ${view.css.fontFamily ? view.css.fontFamily : 'sans-serif'}`;
// 计算行数
let lines = 0;
const linesArray = [];
for (let i = 0; i < textArray.length; ++i) {
const textLength = this.ctx.measureText(textArray[i]).width;
const minWidth = view.css.fontSize.toPx() + paddings[1] + paddings[3];
let partWidth = view.css.width ? view.css.width.toPx(false, this.style.width) - paddings[1] - paddings[3] : textLength;
if (partWidth < minWidth) {

╰︶如此而已
- 粉丝: 5
最新资源
- 基于STC12C5A60S2单片机开发的智能电动消防小车系统_自动寻火源_灭火_返库_计时功能_声音提示_2014山西省大学生电子设计竞赛07题项目_包含出库提示音_火警报警_灭火.zip
- 基于Proteus和AT89C51单片机的多功能电子琴仿真系统设计_包含矩阵键盘输入LCD1602实时显示LED音符指示独立按键音效切换的完整电子琴模拟_用于电子音乐教学演示.zip
- 基于Swift语言开发的QQ音乐iOS客户端完整开源项目_包含音乐播放器界面_歌曲搜索功能_歌词同步显示_本地音乐管理_播放列表创建_个性化推荐系统_夜间模式切换_用户登录注册_音.zip
- 同济大学软件工程专业软件工程管理与经济课程项目基于专有大语言模型的智能文本处理平台_文本摘要生成_批量文件处理_手动编辑_多角色协作审阅_高并发性能优化_政府企业端到端解决方案_.zip
- 活动策划与执行全流程数字化管理系统_晚会会展活动策划_商品设备费用明细管理_客户供应商信息管理_业务查询与财务核算_Excel数据导入导出_宏达数据库开发平台_专为活动承办公司设计.zip
- 外贸企业全流程信息化管理系统_进出口业务管理_外贸单证处理_客户关系维护_货运代理协同_财务结算统计_风险预警提示_适用于各类外贸公司进出口业务全生命周期管理_基于宏达数据库信息管.zip
- 视频采集:开启计算机视觉类项目的首要环节 视频采集作为计算机视觉类项目的初始关键步骤 计算机视觉类项目开展的第一站:视频采集工作 做好视频采集,迈出计算机视觉类项目第一步 视频采集:计算机视觉类项目启
- 基于VictoriaFreSh和ruby-lzma的高效多进程并行压缩工具EXtremeZip_支持目录树打包解包和字节流压缩解压缩_采用CBOR作为文件格式基础_提供类似tar和.zip
- 跨平台个性化桌面壁纸管理系统_实现多终端壁纸同步与智能切换_支持Windows_macOS_iOS_Android全平台覆盖_提供海量高清壁纸资源库_包含用户个性化定制功能_具备自.zip
- 刀具管理系统_企业刀具全生命周期管理_刀具入库登记_领用申请审批_使用归还跟踪_库存预警监控_损耗统计分析_报废处理记录_供应商信息管理_员工使用记录_单位信息维护_入库统计报.zip
- 基于Linux011内核思想设计的轻量级操作系统HJTOS_包含多任务调度内存管理驱动程序文件系统等核心功能_提供完整的操作系统学习框架和开发环境_采用BochsX86虚拟.zip
- 企业级人力资源综合管理系统_人力招聘_人事档案_人事异动_薪资管理_人力开发_日常应用_员工管理_工资发放_培训管理_绩效考核_员工调动_离职管理_复职管理_奖惩登记_证照提醒_生.zip
- 无线接收设备全生命周期智能管理系统_旅游培训公司无线设备接收器发射器借用归还维修报损统计管理_提供设备借出登记归还登记维修登记设备现状报损删除借出单打印功能_支持数据与Excel导.zip
- 面向对象软件开发中23种经典设计模式的完整实现与详细解析_工厂方法模式_抽象工厂模式_建造者模式_原型模式_单例模式_适配器模式_桥接模式_组合模式_装饰模式_外观模式_享元模式_.zip
- songlan666_crmworkspace_7244_1755584871015.zip
- 车险理赔全流程智能管理系统_适用于车辆保险公司的专业理赔管理软件_包含报案录入_查勘定损_核损理算_打印设置_配件管理等核心功能_具有快速辅助录入_操作简单_高效强大的特点_基于宏.zip
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈


