From 691b9633d031f8fd674fbf00f840edeb84da90d0 Mon Sep 17 00:00:00 2001 From: yutent Date: Mon, 18 Dec 2023 18:39:41 +0800 Subject: [PATCH] update --- src/form/image-uploader.js | 229 +++++++++++++++++++++++++------------ 1 file changed, 159 insertions(+), 70 deletions(-) diff --git a/src/form/image-uploader.js b/src/form/image-uploader.js index 5988cea..2de83bf 100644 --- a/src/form/image-uploader.js +++ b/src/form/image-uploader.js @@ -27,6 +27,78 @@ function round(n) { return n } +class ImageTool { + #img + #size = { w: 0, h: 0 } + #canvas = new OffscreenCanvas(1, 1) + #ctx = this.#canvas.getContext('2d') + + #stat = { + x: 1, + y: 1, + deg: 0 + } + + constructor(img, w, h) { + this.#img = img + this.#size = { w, h } + this.#canvas.width = w + this.#canvas.height = h + } + + rotate(v = 0) { + let { w, h } = this.#size + let { deg } = this.#stat + let can = this.#canvas + let ctx = this.#ctx + + deg += v + if (deg === 360) { + deg = 0 + } + + this.#stat.deg = deg + + if (deg % 180 === 0) { + can.width = w + can.height = h + ctx.clearRect(0, 0, w, h) + ctx.translate(w / 2, h / 2) + } else { + can.width = h + can.height = w + ctx.clearRect(0, 0, h, w) + ctx.translate(h / 2, w / 2) + } + ctx.rotate((deg * Math.PI) / 180) + ctx.drawImage(this.#img, -w / 2, -h / 2) + } + + scale(_x = 1, _y = 1) { + let can = this.#canvas + let ctx = this.#ctx + let { w, h } = this.#size + let { x, y } = this.#stat + + x *= _x + y *= _y + + this.#stat = { x, y, deg: 0 } + + can.width = w + can.height = h + + ctx.clearRect(0, 0, w, h) + ctx.scale(x, y) + ctx.translate(x < 1 ? -w : 0, y < 1 ? -h : 0) + ctx.drawImage(this.#img, 0, 0) + } + + export() { + return this.#canvas.convertToBlob({ type: 'image/png' }) + } +} + class Uploader extends Component { static props = { value: 'str!https://static.reduzixun.com/r-time/common/2cc153c8018.webp', @@ -104,6 +176,28 @@ class Uploader extends Component { display: none; } } + + .alpha-bg { + background: linear-gradient( + 45deg, + var(--color-grey-2) 25%, + transparent 25%, + transparent 75%, + var(--color-grey-2) 75%, + var(--color-grey-2) + ), + linear-gradient( + 45deg, + var(--color-grey-2) 25%, + transparent 25%, + transparent 75%, + var(--color-grey-2) 75%, + var(--color-grey-2) + ), + var(--color-plain-2); + background-size: 16px 16px; + background-position: 0 0, 8px 8px; + } `, css` .image-thumb { @@ -212,26 +306,6 @@ class Uploader extends Component { width: 640px; height: 480px; - background: linear-gradient( - 45deg, - var(--color-grey-1) 25%, - transparent 25%, - transparent 75%, - var(--color-grey-1) 75%, - var(--color-grey-1) - ), - linear-gradient( - 45deg, - var(--color-grey-1) 25%, - transparent 25%, - transparent 75%, - var(--color-grey-1) 75%, - var(--color-grey-1) - ); - background-size: 16px 16px; - background-position: 0 0, 8px 8px; - box-shadow: 0 0 0 1px var(--color-grey-2); - canvas { display: block; width: 100%; @@ -313,6 +387,10 @@ class Uploader extends Component { } legend { font-size: 12px; + color: var(--color-red-1); + &.blue { + color: var(--color-blue-3); + } } .button { @@ -346,6 +424,8 @@ class Uploader extends Component { #img #panelShow = false + #cache + // 原图信息 #origin = { x: 0, @@ -354,29 +434,23 @@ class Uploader extends Component { h: 0, // 原图高度 rw: 0, // 渲染宽度 rh: 0, // 渲染高度 - scale: 1 //缩放比例 + scale: 1, //缩放比例 + size: 0 } #cropData = { x: 0, y: 0, w: 300, - h: 300 + h: 300, + size: 0 } #ratio = 1 - #stat = { - blur: false, - scalex: false, - scaley: false, - rotate: 0, - grey: false - } - #fileChange(ev) { let file = ev.target.files[0] ev.target.value = '' - this.#initPanel(URL.createObjectURL(file)) + this.#initPanel(file, true) } #fetchUpload(file) { @@ -463,7 +537,18 @@ class Uploader extends Component { this.#cropData.y = y elem.style.cssText = `left:${x}px;top:${y}px;width:${w}px;height:${h}px;` }) - bind(DOC, 'mouseup', _ => unbind(DOC, 'mousemove', callback), EV_OPTION) + bind( + DOC, + 'mouseup', + _ => { + unbind(DOC, 'mousemove', callback) + this.#export().then(file => { + this.#cropData.size = file.size + this.$requestUpdate() + }) + }, + EV_OPTION + ) } #changeCropRatio(ev) { @@ -493,34 +578,29 @@ class Uploader extends Component { this.#cropData = { w: rw, h: rh, x, y } this.$refs.crop.style.cssText = `left:${x}px;top:${y}px;width:${rw}px;height:${rh}px;` + + this.#export().then(file => { + this.#cropData.size = file.size + this.$requestUpdate() + }) } } - #rotate() { - let ctx = this.#ctx - let { x, y, w, h, rw, rh } = this.#origin + async #rotate() { + this.#cache.rotate(90) + let file = await this.#cache.export() - // ctx.save() - ctx.clearRect(0, 0, WORKSPACE_WIDTH, WORKSPACE_HEIGHT) - ctx.translate(WORKSPACE_WIDTH / 2, WORKSPACE_HEIGHT / 2) - ctx.rotate((90 * Math.PI) / 180) - ctx.drawImage(this.#img, x - w / 2, y - h / 2, rw, rh) - // ctx.restore() - console.log('<><><><>') - } - #scale() {} - - #filter(type) { - if (type === 'blur') { - this.#stat.blur = !this.#stat.blur - let { x, y, rw, rh } = this.#origin - this.#ctx.filter = this.#stat.blur ? 'blur(6px)' : 'none' - // this.#ctx.clearRect(0, 0, WORKSPACE_WIDTH, WORKSPACE_HEIGHT) - this.#ctx.drawImage(this.#img, x, y, rw, rh) - } + this.#initPanel(file) } - #initPanel(url) { + async #scale(axis) { + this.#cache.scale(axis === 'x' ? -1 : 1, axis === 'y' ? -1 : 1) + + let file = await this.#cache.export() + this.#initPanel(file) + } + + #initPanel(file, force) { let img = new Image() this.#img = img @@ -547,13 +627,18 @@ class Uploader extends Component { } let x = (WORKSPACE_WIDTH - rw) / 2 let y = (WORKSPACE_HEIGHT - rh) / 2 - this.#origin = { x, y, w, h, rw, rh, scale } + + if (force) { + this.#cache = new ImageTool(img, w, h) + } + + this.#origin = { x, y, w, h, rw, rh, scale, size: file.size } this.#ctx.drawImage(img, x, y, rw, rh) this.#changeCropRatio({ target: { dataset: { ratio: 1 } } }) this.#panelShow = true this.$requestUpdate() } - img.src = url + img.src = URL.createObjectURL(file) } mounted() { @@ -561,12 +646,19 @@ class Uploader extends Component { fetch(this.value) .then(r => r.blob()) .then(b => { - this.#initPanel(URL.createObjectURL(b)) + this.#initPanel(b, true) }) } render() { let { disabled } = this + let { w: ww, h: hh, scale, size } = this.#origin + let { w, h, size: s } = this.#cropData + + w /= scale + h /= scale + w = ~~w + h = ~~h return html`
@@ -596,7 +688,7 @@ class Uploader extends Component { `}
-
+
原尺寸 - 666x666 + ${ww} x ${hh}
原体积 - 2.33MB + ${parseSize(size)}
- 现尺寸 - 666x666 + 现尺寸 + ${w} x ${h}
- 现体积 - 800KB + 现体积 + ${parseSize(s)}
旋转90度 - 左右翻转 - 上下翻转 - this.#filter('greyscale')} - >去色 + this.#scale('x')}>左右翻转 + this.#scale('y')}>上下翻转 增加水印
- +
` }