update
parent
620ff68a45
commit
691b9633d0
|
@ -27,6 +27,78 @@ function round(n) {
|
||||||
return 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 {
|
class Uploader extends Component {
|
||||||
static props = {
|
static props = {
|
||||||
value: 'str!https://static.reduzixun.com/r-time/common/2cc153c8018.webp',
|
value: 'str!https://static.reduzixun.com/r-time/common/2cc153c8018.webp',
|
||||||
|
@ -104,6 +176,28 @@ class Uploader extends Component {
|
||||||
display: none;
|
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`
|
css`
|
||||||
.image-thumb {
|
.image-thumb {
|
||||||
|
@ -212,26 +306,6 @@ class Uploader extends Component {
|
||||||
width: 640px;
|
width: 640px;
|
||||||
height: 480px;
|
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 {
|
canvas {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -313,6 +387,10 @@ class Uploader extends Component {
|
||||||
}
|
}
|
||||||
legend {
|
legend {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
color: var(--color-red-1);
|
||||||
|
&.blue {
|
||||||
|
color: var(--color-blue-3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
@ -346,6 +424,8 @@ class Uploader extends Component {
|
||||||
#img
|
#img
|
||||||
#panelShow = false
|
#panelShow = false
|
||||||
|
|
||||||
|
#cache
|
||||||
|
|
||||||
// 原图信息
|
// 原图信息
|
||||||
#origin = {
|
#origin = {
|
||||||
x: 0,
|
x: 0,
|
||||||
|
@ -354,29 +434,23 @@ class Uploader extends Component {
|
||||||
h: 0, // 原图高度
|
h: 0, // 原图高度
|
||||||
rw: 0, // 渲染宽度
|
rw: 0, // 渲染宽度
|
||||||
rh: 0, // 渲染高度
|
rh: 0, // 渲染高度
|
||||||
scale: 1 //缩放比例
|
scale: 1, //缩放比例
|
||||||
|
size: 0
|
||||||
}
|
}
|
||||||
#cropData = {
|
#cropData = {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
w: 300,
|
w: 300,
|
||||||
h: 300
|
h: 300,
|
||||||
|
size: 0
|
||||||
}
|
}
|
||||||
#ratio = 1
|
#ratio = 1
|
||||||
|
|
||||||
#stat = {
|
|
||||||
blur: false,
|
|
||||||
scalex: false,
|
|
||||||
scaley: false,
|
|
||||||
rotate: 0,
|
|
||||||
grey: false
|
|
||||||
}
|
|
||||||
|
|
||||||
#fileChange(ev) {
|
#fileChange(ev) {
|
||||||
let file = ev.target.files[0]
|
let file = ev.target.files[0]
|
||||||
ev.target.value = ''
|
ev.target.value = ''
|
||||||
|
|
||||||
this.#initPanel(URL.createObjectURL(file))
|
this.#initPanel(file, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#fetchUpload(file) {
|
#fetchUpload(file) {
|
||||||
|
@ -463,7 +537,18 @@ class Uploader extends Component {
|
||||||
this.#cropData.y = y
|
this.#cropData.y = y
|
||||||
elem.style.cssText = `left:${x}px;top:${y}px;width:${w}px;height:${h}px;`
|
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) {
|
#changeCropRatio(ev) {
|
||||||
|
@ -493,34 +578,29 @@ class Uploader extends Component {
|
||||||
this.#cropData = { w: rw, h: rh, x, y }
|
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.$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() {
|
async #rotate() {
|
||||||
let ctx = this.#ctx
|
this.#cache.rotate(90)
|
||||||
let { x, y, w, h, rw, rh } = this.#origin
|
let file = await this.#cache.export()
|
||||||
|
|
||||||
// ctx.save()
|
this.#initPanel(file)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#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()
|
let img = new Image()
|
||||||
|
|
||||||
this.#img = img
|
this.#img = img
|
||||||
|
@ -547,13 +627,18 @@ class Uploader extends Component {
|
||||||
}
|
}
|
||||||
let x = (WORKSPACE_WIDTH - rw) / 2
|
let x = (WORKSPACE_WIDTH - rw) / 2
|
||||||
let y = (WORKSPACE_HEIGHT - rh) / 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.#ctx.drawImage(img, x, y, rw, rh)
|
||||||
this.#changeCropRatio({ target: { dataset: { ratio: 1 } } })
|
this.#changeCropRatio({ target: { dataset: { ratio: 1 } } })
|
||||||
this.#panelShow = true
|
this.#panelShow = true
|
||||||
this.$requestUpdate()
|
this.$requestUpdate()
|
||||||
}
|
}
|
||||||
img.src = url
|
img.src = URL.createObjectURL(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -561,12 +646,19 @@ class Uploader extends Component {
|
||||||
fetch(this.value)
|
fetch(this.value)
|
||||||
.then(r => r.blob())
|
.then(r => r.blob())
|
||||||
.then(b => {
|
.then(b => {
|
||||||
this.#initPanel(URL.createObjectURL(b))
|
this.#initPanel(b, true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { disabled } = this
|
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`
|
return html`
|
||||||
<main class="container">
|
<main class="container">
|
||||||
|
@ -596,7 +688,7 @@ class Uploader extends Component {
|
||||||
`}
|
`}
|
||||||
|
|
||||||
<div class="edit-panel ${this.#panelShow ? 'show' : ''}">
|
<div class="edit-panel ${this.#panelShow ? 'show' : ''}">
|
||||||
<div class="workspace">
|
<div class="workspace alpha-bg">
|
||||||
<canvas ref="can" width="640" height="480"></canvas>
|
<canvas ref="can" width="640" height="480"></canvas>
|
||||||
<div
|
<div
|
||||||
ref="crop"
|
ref="crop"
|
||||||
|
@ -620,19 +712,19 @@ class Uploader extends Component {
|
||||||
</div>
|
</div>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>原尺寸</legend>
|
<legend>原尺寸</legend>
|
||||||
<span>666x666</span>
|
<span>${ww} x ${hh}</span>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>原体积</legend>
|
<legend>原体积</legend>
|
||||||
<span>2.33MB</span>
|
<span>${parseSize(size)}</span>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>现尺寸</legend>
|
<legend class="blue">现尺寸</legend>
|
||||||
<span>666x666</span>
|
<span>${w} x ${h}</span>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>现体积</legend>
|
<legend class="blue">现体积</legend>
|
||||||
<span>800KB</span>
|
<span>${parseSize(s)}</span>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<span class="button" @click=${this.#cancel}
|
<span class="button" @click=${this.#cancel}
|
||||||
|
@ -648,18 +740,15 @@ class Uploader extends Component {
|
||||||
|
|
||||||
<footer class="toolbar">
|
<footer class="toolbar">
|
||||||
<span class="button" @click=${this.#rotate}>旋转90度</span>
|
<span class="button" @click=${this.#rotate}>旋转90度</span>
|
||||||
<span class="button" @click=${this.#scale}>左右翻转</span>
|
<span class="button" @click=${_ => this.#scale('x')}>左右翻转</span>
|
||||||
<span class="button" @click=${this.#scale}>上下翻转</span>
|
<span class="button" @click=${_ => this.#scale('y')}>上下翻转</span>
|
||||||
<span class="button" @click=${_ => this.#filter('greyscale')}
|
|
||||||
>去色</span
|
|
||||||
>
|
|
||||||
<span class="button">增加水印</span>
|
<span class="button">增加水印</span>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div class="preview" ref="view" @click.self=${this.#closePreview}>
|
<div class="preview" ref="view" @click.self=${this.#closePreview}>
|
||||||
<img />
|
<img class="alpha-bg" />
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue