@meta2d/core
Version:
@meta2d/core: Powerful, Beautiful, Simple, Open - Web-Based 2D At Its Best .
451 lines • 17.9 kB
JavaScript
import { ctxFlip, ctxRotate, drawImage, setGlobalAlpha, getParent, CanvasLayer, } from '../pen';
import { rgba } from '../utils';
import { createOffscreen } from './offscreen';
export class CanvasImage {
parentElement;
store;
isBottom;
canvas = document.createElement('canvas');
/**
* 非图片的绘制
* isBottom true 指背景颜色,背景网格
* isBottom false 指 标尺
*/
otherOffsreen = createOffscreen(); // 非图片的
offscreen = createOffscreen();
animateOffsScreen = createOffscreen();
fitOffscreen = createOffscreen();
fitFlag = false; //开启自定义填充
currentFit;
activeFit;
constructor(parentElement, store, isBottom) {
this.parentElement = parentElement;
this.store = store;
this.isBottom = isBottom;
parentElement.appendChild(this.canvas);
this.canvas.style.backgroundRepeat = 'no-repeat';
this.canvas.style.backgroundSize = '100% 100%';
this.canvas.style.position = 'absolute';
this.canvas.style.top = '0';
this.canvas.style.left = '0';
}
resize(w, h) {
this.canvas.style.width = w + 'px';
this.canvas.style.height = h + 'px';
w = (w * this.store.dpiRatio) | 0;
h = (h * this.store.dpiRatio) | 0;
this.canvas.width = w;
this.canvas.height = h;
this.otherOffsreen.width = w;
this.otherOffsreen.height = h;
this.offscreen.width = w;
this.offscreen.height = h;
this.animateOffsScreen.width = w;
this.animateOffsScreen.height = h;
this.fitOffscreen.width = w;
this.fitOffscreen.height = h;
this.otherOffsreen
.getContext('2d')
.scale(this.store.dpiRatio, this.store.dpiRatio);
this.otherOffsreen.getContext('2d').textBaseline = 'middle';
this.offscreen
.getContext('2d')
.scale(this.store.dpiRatio, this.store.dpiRatio);
this.offscreen.getContext('2d').textBaseline = 'middle';
this.animateOffsScreen
.getContext('2d')
.scale(this.store.dpiRatio, this.store.dpiRatio);
this.animateOffsScreen.getContext('2d').textBaseline = 'middle';
this.fitOffscreen
.getContext('2d')
.scale(this.store.dpiRatio, this.store.dpiRatio);
this.fitOffscreen.getContext('2d').textBaseline = 'middle';
this.init();
}
init() {
this.offscreen
.getContext('2d')
.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.animateOffsScreen
.getContext('2d')
.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.fitOffscreen
.getContext('2d')
.clearRect(0, 0, this.canvas.width, this.canvas.height);
for (const pen of this.store.data.pens) {
if (this.hasImage(pen)) {
// 只影响本层的
pen.calculative.imageDrawed = false;
}
}
if (this.isBottom) {
this.store.patchFlagsBackground = true;
}
else {
this.store.patchFlagsTop = true;
}
}
clear() {
this.otherOffsreen
.getContext('2d')
.clearRect(0, 0, this.otherOffsreen.width, this.otherOffsreen.height);
this.offscreen
.getContext('2d')
.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.animateOffsScreen
.getContext('2d')
.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.fitOffscreen
.getContext('2d')
.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.canvas
.getContext('2d')
.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
hasImage(pen) {
pen.calculative.hasImage =
pen.calculative &&
pen.calculative.inView &&
// !pen.isBottom == !this.isBottom && // undefined == false 结果 false
((this.isBottom && pen.canvasLayer === CanvasLayer.CanvasImageBottom) ||
(!this.isBottom && pen.canvasLayer === CanvasLayer.CanvasImage)) &&
pen.image &&
pen.calculative.img &&
pen.name !== 'gif';
return pen.calculative.hasImage;
}
render() {
let patchFlags = false;
let patchFlagsAnimate = false;
for (const pen of this.store.data.pens) {
if (this.hasImage(pen)) {
if (this.store.animates.has(pen)) {
patchFlagsAnimate = true;
}
else if (!pen.calculative.imageDrawed) {
patchFlags = true;
}
if (pen.parentId && this.store.animates.has(getParent(pen, true))) {
patchFlagsAnimate = true;
}
}
}
const patchFlagsBackground = this.store.patchFlagsBackground;
// if (patchFlagsBackground && this.isBottom) {
// const ctx = this.otherOffsreen.getContext('2d');
// ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// const width = this.store.data.width || this.store.options.width;
// const height = this.store.data.height || this.store.options.height;
// const x = this.store.data.x || this.store.options.x;
// const y = this.store.data.y || this.store.options.y;
// if (width && height && this.store.bkImg) {
// ctx.save();
// ctx.drawImage(
// this.store.bkImg,
// this.store.data.origin.x + x,
// this.store.data.origin.y + y,
// width * this.store.data.scale,
// height * this.store.data.scale
// );
// ctx.restore();
// }
// const background =
// this.store.data.background ||
// (this.store.bkImg ? undefined : this.store.options.background);
// if (background) {
// ctx.save();
// ctx.fillStyle = background;
// if (width && height) {
// ctx.fillRect(
// this.store.data.origin.x + x,
// this.store.data.origin.y + y,
// width * this.store.data.scale,
// height * this.store.data.scale
// );
// } else {
// ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
// }
// ctx.restore();
// }
// this.renderGrid(ctx);
// }
const patchFlagsTop = this.store.patchFlagsTop;
if (patchFlagsTop && !this.isBottom) {
const ctx = this.otherOffsreen.getContext('2d');
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.renderRule(ctx);
}
// 从有图片画布层切换到无图片画布
const patchFlagsLast = this.store.patchFlagsLast;
if (patchFlagsLast) {
const ctx = this.offscreen.getContext('2d');
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
if (patchFlags) {
const ctx = this.offscreen.getContext('2d');
ctx.save();
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
ctx.translate(this.store.data.x, this.store.data.y);
for (const pen of this.store.data.pens) {
//pen.calculative.imageDrawed 只用于判断是否需要重绘整块画布,不用于判断改图片节点是否绘制过
if (!pen.calculative.hasImage ||
this.store.animates.has(pen) ||
this.store.animates.has(getParent(pen, true))) {
continue;
}
// if (pen.template) {
if (pen.canvasLayer === CanvasLayer.CanvasTemplate) {
continue;
}
// if (pen.name === 'combine' && !pen.draw){
// continue;
// }
pen.calculative.imageDrawed = true;
ctx.save();
ctxFlip(ctx, pen);
if (pen.rotateByRoot || pen.calculative.rotate) {
ctxRotate(ctx, pen);
}
setGlobalAlpha(ctx, pen);
drawImage(ctx, pen);
ctx.restore();
}
ctx.restore();
}
if (patchFlagsAnimate) {
const ctx = this.animateOffsScreen.getContext('2d');
ctx.save();
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
ctx.translate(this.store.data.x, this.store.data.y);
for (const pen of this.store.animates) {
if (!pen.calculative.hasImage) {
continue;
}
// if (pen.template) {
if (pen.canvasLayer === CanvasLayer.CanvasTemplate) {
continue;
}
if (pen.visible === false || pen.calculative.visible === false) {
//动画控制显示隐藏
continue;
}
pen.calculative.imageDrawed = true;
ctx.save();
ctxFlip(ctx, pen);
if (pen.rotateByRoot || pen.calculative.rotate) {
ctxRotate(ctx, pen);
}
setGlobalAlpha(ctx, pen);
drawImage(ctx, pen);
ctx.restore();
}
//图片组合节点 动画
for (const pen of this.store.data.pens) {
if (!pen.calculative.hasImage || !pen.parentId) {
continue;
}
// if (pen.template) {
if (pen.canvasLayer === CanvasLayer.CanvasTemplate) {
continue;
}
if (pen.visible === false || pen.calculative.visible === false) {
continue;
}
if (this.store.animates.has(getParent(pen, true))) {
pen.calculative.imageDrawed = true;
ctx.save();
ctxFlip(ctx, pen);
if (pen.rotateByRoot || pen.calculative.rotate) {
ctxRotate(ctx, pen);
}
setGlobalAlpha(ctx, pen);
drawImage(ctx, pen);
ctx.restore();
}
}
ctx.restore();
}
if (!this.isBottom && !this.store.data.locked && this.fitFlag) {
const width = (this.store.data.width || this.store.options.width) *
this.store.data.scale;
const height = (this.store.data.height || this.store.options.height) *
this.store.data.scale;
const x = this.store.data.origin.x + this.store.data.x ||
this.store.options.x ||
0;
const y = this.store.data.origin.y + this.store.data.y ||
this.store.options.y ||
0;
const ctx = this.fitOffscreen.getContext('2d');
ctx.save();
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
ctx.fillStyle = '#ffffff66';
ctx.strokeStyle = this.store.styles.activeColor;
this.store.data.fits?.forEach((item, index) => {
ctx.fillRect(x + width * item.x, y + height * item.y, width * item.width, height * item.height);
if (item.active) {
ctx.strokeRect(x + width * item.x, y + height * item.y, width * item.width, height * item.height);
}
});
ctx.restore();
}
if (patchFlags ||
patchFlagsAnimate ||
(patchFlagsBackground && this.isBottom) ||
// this.isBottom ||
(patchFlagsTop && !this.isBottom)) {
const ctxCanvas = this.canvas.getContext('2d');
ctxCanvas.clearRect(0, 0, this.canvas.width, this.canvas.height);
if (this.isBottom) {
// ctxCanvas.drawImage(
// this.otherOffsreen,
// 0,
// 0,
// this.canvas.width,
// this.canvas.height
// );
this.store.patchFlagsBackground = false;
}
ctxCanvas.drawImage(this.offscreen, 0, 0, this.canvas.width, this.canvas.height);
ctxCanvas.drawImage(this.animateOffsScreen, 0, 0, this.canvas.width, this.canvas.height);
if (!this.isBottom) {
ctxCanvas.drawImage(this.otherOffsreen, 0, 0, this.canvas.width, this.canvas.height);
this.store.patchFlagsTop = false;
if (!this.store.data.locked && this.fitFlag) {
ctxCanvas.drawImage(this.fitOffscreen, 0, 0, this.canvas.width, this.canvas.height);
}
}
}
}
// renderGrid(
// ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D
// ) {
// const { data, options } = this.store;
// const { grid, gridRotate, gridColor, gridSize, scale } = data;
// if (!(grid ?? options.grid)) {
// // grid false 时不绘制, undefined 时看 options.grid
// return;
// }
// ctx.save();
// const { width, height } = this.canvas;
// if (gridRotate) {
// ctx.translate(width / 2, height / 2);
// ctx.rotate((gridRotate * Math.PI) / 180);
// ctx.translate(-width / 2, -height / 2);
// }
// ctx.lineWidth = 1;
// ctx.strokeStyle = gridColor || options.gridColor;
// ctx.beginPath();
// const size = (gridSize || options.gridSize) * scale;
// const longSide = Math.max(width, height);
// const count = Math.ceil(longSide / size);
// for (let i = -size * count; i < longSide * 2; i += size) {
// ctx.moveTo(i, -longSide);
// ctx.lineTo(i, longSide * 2);
// }
// for (let i = -size * count; i < longSide * 2; i += size) {
// ctx.moveTo(-longSide, i);
// ctx.lineTo(longSide * 2, i);
// }
// ctx.stroke();
// ctx.restore();
// }
renderRule(ctx) {
const { data, options } = this.store;
const { rule, ruleColor, scale, origin } = data;
if (!(rule ?? options.rule)) {
// rule false 时不绘制, undefined 时看 options.rule
return;
}
const span = scale * 10;
ctx.save();
const finalRuleColor = ruleColor || options.ruleColor || "#ccc";
ctx.strokeStyle = rgba(finalRuleColor, 0.7);
const x = origin.x + data.x;
const y = origin.y + data.y;
const { width, height } = this.canvas;
let h = options.ruleOptions?.height || 20;
if (options.ruleOptions?.background) {
//背景颜色
ctx.beginPath();
ctx.fillStyle = options.ruleOptions?.background;
ctx.rect(0, 0, width, h);
ctx.fill();
ctx.rect(0, 0, h, height);
ctx.fill();
}
if (options.ruleOptions?.underline) {
ctx.beginPath();
ctx.fillStyle = rgba(finalRuleColor, 0.7);
ctx.moveTo(0, h);
ctx.lineTo(width, h);
ctx.stroke();
ctx.moveTo(h, 0);
ctx.lineTo(h, height);
ctx.stroke();
}
let b_y = h / 4;
if (options.ruleOptions?.baseline === "bottom") {
b_y = h * 3 / 4;
}
// horizontal rule
ctx.beginPath();
ctx.lineWidth = h / 2;
ctx.lineDashOffset = -x % span;
ctx.setLineDash([1, span - 1]);
ctx.moveTo(0, b_y);
ctx.lineTo(width, b_y);
ctx.stroke();
// vertical rule
ctx.beginPath();
ctx.lineDashOffset = -y % span;
ctx.moveTo(b_y, 0);
ctx.lineTo(b_y, height);
ctx.stroke();
// the big rule
ctx.strokeStyle = finalRuleColor;
ctx.beginPath();
ctx.lineWidth = h;
ctx.lineDashOffset = -x % (span * 10);
ctx.setLineDash([1, span * 10 - 1]);
ctx.moveTo(0, h / 2);
ctx.lineTo(width, h / 2);
ctx.stroke();
ctx.beginPath();
ctx.lineDashOffset = -y % (span * 10);
ctx.moveTo(h / 2, 0);
ctx.lineTo(h / 2, height);
ctx.stroke();
ctx.beginPath();
ctx.fillStyle = options.ruleOptions?.textColor || ctx.strokeStyle;
let text = 0 - Math.floor(x / span / 10) * 100;
let textTop = options.ruleOptions?.textTop || 16;
let textLeft = options.ruleOptions?.textLeft || 4;
if (x < 0) {
text -= 100;
}
for (let i = x % (span * 10); i < width; i += 10 * span, text += 100) {
if (span < 3 && text % 500) {
continue;
}
ctx.fillText(text.toString(), i + textLeft, textTop);
}
text = 0 - Math.floor(y / span / 10) * 100;
if (y < 0) {
text -= 100;
}
for (let i = y % (span * 10); i < height; i += 10 * span, text += 100) {
if (span < 3 && text % 500) {
continue;
}
ctx.save();
ctx.beginPath();
ctx.translate(textTop, i - textLeft);
ctx.rotate((270 * Math.PI) / 180);
ctx.fillText(text.toString(), 0, 0);
ctx.restore();
}
ctx.restore();
}
}
//# sourceMappingURL=canvasImage.js.map