update
parent
620ff68a45
commit
691b9633d0
|
@ -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`
|
||||
<main class="container">
|
||||
|
@ -596,7 +688,7 @@ class Uploader extends Component {
|
|||
`}
|
||||
|
||||
<div class="edit-panel ${this.#panelShow ? 'show' : ''}">
|
||||
<div class="workspace">
|
||||
<div class="workspace alpha-bg">
|
||||
<canvas ref="can" width="640" height="480"></canvas>
|
||||
<div
|
||||
ref="crop"
|
||||
|
@ -620,19 +712,19 @@ class Uploader extends Component {
|
|||
</div>
|
||||
<fieldset>
|
||||
<legend>原尺寸</legend>
|
||||
<span>666x666</span>
|
||||
<span>${ww} x ${hh}</span>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>原体积</legend>
|
||||
<span>2.33MB</span>
|
||||
<span>${parseSize(size)}</span>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>现尺寸</legend>
|
||||
<span>666x666</span>
|
||||
<legend class="blue">现尺寸</legend>
|
||||
<span>${w} x ${h}</span>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>现体积</legend>
|
||||
<span>800KB</span>
|
||||
<legend class="blue">现体积</legend>
|
||||
<span>${parseSize(s)}</span>
|
||||
</fieldset>
|
||||
|
||||
<span class="button" @click=${this.#cancel}
|
||||
|
@ -648,18 +740,15 @@ class Uploader extends Component {
|
|||
|
||||
<footer class="toolbar">
|
||||
<span class="button" @click=${this.#rotate}>旋转90度</span>
|
||||
<span class="button" @click=${this.#scale}>左右翻转</span>
|
||||
<span class="button" @click=${this.#scale}>上下翻转</span>
|
||||
<span class="button" @click=${_ => this.#filter('greyscale')}
|
||||
>去色</span
|
||||
>
|
||||
<span class="button" @click=${_ => this.#scale('x')}>左右翻转</span>
|
||||
<span class="button" @click=${_ => this.#scale('y')}>上下翻转</span>
|
||||
<span class="button">增加水印</span>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div class="preview" ref="view" @click.self=${this.#closePreview}>
|
||||
<img />
|
||||
<img class="alpha-bg" />
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue