285 lines
9.5 KiB
JavaScript
285 lines
9.5 KiB
JavaScript
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __export = (target, all) => {
|
|
for (var name in all)
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
};
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
for (let key of __getOwnPropNames(from))
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
}
|
|
return to;
|
|
};
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
|
|
// src/ImageEditor/viewer/render.ts
|
|
var render_exports = {};
|
|
__export(render_exports, {
|
|
default: () => render_default
|
|
});
|
|
module.exports = __toCommonJS(render_exports);
|
|
var import_css = require("rc-util/lib/Dom/css");
|
|
var import_func = require("@zhst/func");
|
|
var import_utils = require("../utils");
|
|
var import_constants = require("./constants");
|
|
var render_default = {
|
|
// store
|
|
image: null,
|
|
canvas: null,
|
|
containerData: {
|
|
width: 0,
|
|
height: 0
|
|
},
|
|
animationFrame: null,
|
|
backgroundColor: "#fff",
|
|
targetTransform: {
|
|
translateX: 0,
|
|
translateY: 0,
|
|
scale: 0,
|
|
rotate: 0
|
|
// rotate: 90,
|
|
},
|
|
//method
|
|
async render() {
|
|
await this.initImg();
|
|
this.initCanvas();
|
|
this.startRaf();
|
|
},
|
|
initImg() {
|
|
return new Promise((resolve, reject) => {
|
|
const { image: propImage } = this.options;
|
|
if ((0, import_func.isString)(propImage)) {
|
|
const image = new Image();
|
|
image.crossOrigin = "anonymous";
|
|
image.src = propImage;
|
|
image.onload = () => {
|
|
this.image = image;
|
|
resolve(image);
|
|
};
|
|
image.onerror = (err) => {
|
|
reject(err);
|
|
};
|
|
} else {
|
|
this.image = propImage;
|
|
resolve(propImage);
|
|
}
|
|
});
|
|
},
|
|
initCanvas() {
|
|
if (!this.image)
|
|
return;
|
|
const { element, canvas, limit = {}, options } = this;
|
|
const containerData = {
|
|
width: (0, import_css.getOuterWidth)(element),
|
|
height: (0, import_css.getOuterHeight)(element)
|
|
};
|
|
this.containerData = containerData;
|
|
(0, import_css.set)(canvas, containerData);
|
|
canvas.width = containerData.width;
|
|
canvas.height = containerData.height;
|
|
const fitTransform = this.calcFitScreen();
|
|
this.targetTransform = Object.assign({}, this.targetTransform, fitTransform);
|
|
(0, import_utils.dispatchEvent)(this.element, import_constants.EVENT_VIEWER_TRANSFORM_CHANGE, (0, import_func.cloneDeep)(this.targetTransform));
|
|
const { fitScaleAsMinScale = false } = options;
|
|
if (fitScaleAsMinScale) {
|
|
this.limit = Object.assign({ minScale: this.targetTransform.scale }, this.limit);
|
|
}
|
|
},
|
|
startRaf() {
|
|
window.cancelAnimationFrame(this.animationFrame);
|
|
const loop = () => {
|
|
this.renderCanvas();
|
|
window.cancelAnimationFrame(this.animationFrame);
|
|
this.animationFrame = window.requestAnimationFrame(loop);
|
|
};
|
|
loop();
|
|
},
|
|
renderCanvas(_ctx) {
|
|
if (!this.image || !this.canvas)
|
|
return;
|
|
const { containerData, canvas, targetTransform, options } = this;
|
|
const { translateX, translateY, scale, rotate } = targetTransform;
|
|
const ctx = _ctx ? _ctx : canvas.getContext("2d");
|
|
ctx.clearRect(0, 0, containerData.width, containerData.height);
|
|
ctx.fillStyle = options.backgroundColor;
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
ctx.save();
|
|
ctx.setTransform(scale, 0, 0, scale, translateX, translateY);
|
|
const centerX = this.image.width / 2;
|
|
const centerY = this.image.height / 2;
|
|
ctx.translate(centerX, centerY);
|
|
ctx.rotate(rotate / 180 * Math.PI);
|
|
ctx.translate(-centerX, -centerY);
|
|
ctx.drawImage(this.image, 0, 0);
|
|
ctx.restore();
|
|
ctx.save();
|
|
this.renderShape(ctx);
|
|
ctx.restore();
|
|
},
|
|
scaleTo(offsetScale) {
|
|
const { containerData } = this;
|
|
this.calcTransform({
|
|
scaleCenter: {
|
|
x: containerData.width / 2,
|
|
y: containerData.height / 2,
|
|
step: offsetScale
|
|
}
|
|
});
|
|
},
|
|
//暂时只支持90deg旋转 否则limit 不能做
|
|
rotateTo(T) {
|
|
if (!this.image)
|
|
return;
|
|
const { targetTransform } = this;
|
|
const { rotate } = targetTransform;
|
|
const newDeg = (0, import_func.isFunction)(T) ? T(rotate) : T;
|
|
this.calcTransform({
|
|
rotate: newDeg
|
|
});
|
|
},
|
|
reset() {
|
|
this.targetTransform.rotate = 0;
|
|
const fitTransform = this.calcFitScreen();
|
|
this.targetTransform = Object.assign({}, this.targetTransform, fitTransform);
|
|
(0, import_utils.dispatchEvent)(this.element, import_constants.EVENT_VIEWER_TRANSFORM_CHANGE, (0, import_func.cloneDeep)(this.targetTransform));
|
|
},
|
|
getImgSize() {
|
|
if (!this.image)
|
|
return;
|
|
return { w: this.image.width, h: this.image.height };
|
|
},
|
|
calcFitScreen() {
|
|
if (!this.image)
|
|
return;
|
|
const w = this.containerData.width;
|
|
const h = this.containerData.height;
|
|
const iw = this.image.width;
|
|
const ih = this.image.height;
|
|
const { rotate = 0 } = this.targetTransform;
|
|
const wToh = w / h;
|
|
let imgwToh = iw / ih;
|
|
if (rotate % 180 !== 0) {
|
|
imgwToh = ih / iw;
|
|
}
|
|
let scale;
|
|
let translateX = 0;
|
|
let translateY = 0;
|
|
if (imgwToh > wToh) {
|
|
scale = w / iw;
|
|
translateX = 0;
|
|
translateY = (h - ih * scale) / 2;
|
|
if (rotate % 180 !== 0) {
|
|
scale = w / ih;
|
|
translateX = (ih - iw) / 2 * scale;
|
|
translateY = (h - iw * scale) / 2 + (iw - ih) / 2 * scale;
|
|
}
|
|
} else {
|
|
scale = h / ih;
|
|
translateX = (w - iw * scale) / 2;
|
|
translateY = 0;
|
|
if (rotate % 180 !== 0) {
|
|
scale = h / iw;
|
|
translateX = (w - ih * scale) / 2 + (ih - iw) / 2 * scale;
|
|
translateY = (iw - ih) / 2 * scale;
|
|
}
|
|
}
|
|
const fitTransform = {
|
|
translateX,
|
|
translateY,
|
|
scale
|
|
};
|
|
return fitTransform;
|
|
},
|
|
calcTransform(newTransform, cropBox) {
|
|
if (!this.image)
|
|
return;
|
|
const { translateX, translateY, scaleCenter, rotate } = newTransform;
|
|
if (!(0, import_func.isNil)(scaleCenter)) {
|
|
const { targetTransform: targetTransform2, limit = {} } = this;
|
|
let { minScale = 0.1, maxScale = Number.MAX_VALUE } = limit;
|
|
if (cropBox) {
|
|
let { width: iw, height: ih } = this.image;
|
|
if (targetTransform2.rotate % 180 !== 0)
|
|
[iw, ih] = [ih, iw];
|
|
const { width: cw, height: ch } = cropBox;
|
|
const corpMinScale = Math.max(cw / iw, ch / ih);
|
|
minScale = Math.max(corpMinScale, minScale);
|
|
}
|
|
const { x, y, step } = scaleCenter;
|
|
const preScale = targetTransform2.scale;
|
|
let newScale = targetTransform2.scale + step;
|
|
newScale = Math.min(maxScale, Math.max(newScale, minScale));
|
|
const newStep = newScale - preScale;
|
|
if (newScale != preScale) {
|
|
const offsetX = (x - targetTransform2.translateX) / targetTransform2.scale * newStep;
|
|
const offsetY = (y - targetTransform2.translateY) / targetTransform2.scale * newStep;
|
|
this.targetTransform.translateX = this.targetTransform.translateX - offsetX;
|
|
this.targetTransform.translateY = this.targetTransform.translateY - offsetY;
|
|
}
|
|
this.targetTransform.scale = newScale;
|
|
}
|
|
if (!(0, import_func.isNil)(translateX)) {
|
|
this.targetTransform.translateX += translateX;
|
|
}
|
|
if (!(0, import_func.isNil)(translateY)) {
|
|
this.targetTransform.translateY += translateY;
|
|
}
|
|
if (!(0, import_func.isNil)(rotate)) {
|
|
this.targetTransform.rotate = rotate;
|
|
}
|
|
const { targetTransform, containerData } = this;
|
|
const minMargin = 50;
|
|
let imgWidth = this.image.width;
|
|
let imgHeight = this.image.height;
|
|
if (this.targetTransform.rotate % 180 !== 0) {
|
|
imgWidth = this.image.height;
|
|
imgHeight = this.image.width;
|
|
}
|
|
const minX = minMargin - imgWidth * targetTransform.scale;
|
|
const maxX = containerData.width - minMargin;
|
|
const minY = minMargin - imgHeight * targetTransform.scale;
|
|
const maxY = containerData.height - minMargin;
|
|
this.targetTransform.translateX = Math.min(
|
|
maxX,
|
|
Math.max(this.targetTransform.translateX, minX)
|
|
);
|
|
this.targetTransform.translateY = Math.min(
|
|
maxY,
|
|
Math.max(this.targetTransform.translateY, minY)
|
|
);
|
|
if (cropBox) {
|
|
const { width: cw, height: ch, top, left } = cropBox;
|
|
const { width: iw, height: ih } = this.image;
|
|
let imgLeftTop = { x: 0, y: 0 };
|
|
let imgRightBottom = { x: iw, y: ih };
|
|
if (this.targetTransform.rotate % 180 !== 0) {
|
|
imgLeftTop = {
|
|
x: (iw - ih) / 2,
|
|
y: (ih - iw) / 2
|
|
};
|
|
imgRightBottom = {
|
|
x: (iw + ih) / 2,
|
|
y: (iw + ih) / 2
|
|
};
|
|
}
|
|
const maxX2 = left - imgLeftTop.x * this.targetTransform.scale;
|
|
const maxY2 = top - imgLeftTop.y * this.targetTransform.scale;
|
|
const minX2 = left + cw - imgRightBottom.x * this.targetTransform.scale;
|
|
const minY2 = top + ch - imgRightBottom.y * this.targetTransform.scale;
|
|
this.targetTransform.translateX = Math.min(
|
|
maxX2,
|
|
Math.max(this.targetTransform.translateX, minX2)
|
|
);
|
|
this.targetTransform.translateY = Math.min(
|
|
maxY2,
|
|
Math.max(this.targetTransform.translateY, minY2)
|
|
);
|
|
}
|
|
(0, import_utils.dispatchEvent)(this.element, import_constants.EVENT_VIEWER_TRANSFORM_CHANGE, (0, import_func.cloneDeep)(this.targetTransform));
|
|
}
|
|
};
|