Merge pull request #48 in YR/star-web-components from gauss-canvas to master
* commit 'b6d1047169261cd4fdf1d138abf5f7b8780c9bad': TASK: #113126 - add gauss blur component
This commit is contained in:
commit
bb6692cd37
|
@ -24,3 +24,4 @@
|
|||
- add function for dragging icon into container
|
||||
- add homescreen function for storing apps' order
|
||||
- fix bugs of container
|
||||
- add gauss blur component
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
content="width=device-width, user-scalable=no, initial-scale=1.0"
|
||||
/>
|
||||
<title>Star Web Components</title>
|
||||
<script type="module" src="./start-element.js"></script>
|
||||
<script type="module" src="./star-element.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- main -->
|
||||
|
|
|
@ -21,6 +21,11 @@
|
|||
"description": "StarWeb组件"
|
||||
}
|
||||
},
|
||||
"permissions": {
|
||||
"settings": {
|
||||
"access": "readwrite"
|
||||
}
|
||||
},
|
||||
"core": true,
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
## 高斯模糊小组件
|
||||
|
||||
小组件为解决用 `Css` 属性进行高斯模糊后,浏览器卡顿的问题。原理是用一个 `canvas` 将根据不同 `sigma` 值进行高斯模糊处理后的图展示出来。
|
||||
|
||||
该组件的模糊算法当 `sigma` 越高时 ,其处理模糊的速度越快,越低时处理速度越慢,然而 `sigma` 越低,模糊程度越小,与原图越类似,可以用降低分辨率的方法替换模糊算法。因此组件设定了一个门槛值 `threshold` ,使用者可以根据机器性能和图片质量选择门槛值的高低。
|
||||
|
||||
因为模糊算法耗时受 `sigma` 值和图片分辨率的影响,无法准确掌控,因此为了不阻塞主线程的渲染工作,模糊算法需要放入 `Web worker` 中运行。
|
||||
|
||||
### 属性
|
||||
|
||||
- `src`: 要高斯模糊的图片URL
|
||||
- `sigma`: 高斯模糊程度系数,除了第一次传值时不会有模糊渐变,之后传值时图片模糊会呈渐变,如若不需要动画则调用方法 `showImmediately` 并传值
|
||||
- `threshold`: 当 `sigma` 大于该值时,组件采用模糊算法,否则采用降低分辨率的方法模糊图片,默认值为1
|
||||
- `during`: 模糊渐变的最长时间,单位为 `ms`,默认值为 500
|
||||
- `bezier`: 模糊渐变的贝塞尔系数,接受参数为一个有四个数字元素的数组,默认值为 `[0.19, 1, 0.22, 1]`
|
||||
|
||||
### 使用
|
||||
|
||||
注意,请不要使用跨域图片资源,否则无法转化为 `ImageData` 进行模糊计算
|
||||
|
||||
|
||||
```html
|
||||
<gauss-canvas src="./test.png" sigma="1"></gauss-canvas>
|
||||
```
|
||||
|
||||
```js
|
||||
import "@star-web-element/gauss"
|
||||
const canvas = document.querySelector('gauss-canvas');
|
||||
|
||||
canvas.addEventListener('click', () => {
|
||||
// 会有模糊渐变
|
||||
canvas.sigma ^= 10
|
||||
})
|
||||
|
||||
canvas.addEventListener('mousedown', handleEvent);
|
||||
canvas.addEventListener('mousemove', handleEvent);
|
||||
canvas.addEventListener('mouseup', handleEvent);
|
||||
|
||||
let mouseData = {
|
||||
start: 0,
|
||||
moveDistance: 0,
|
||||
}
|
||||
function handleEvent(evt) {
|
||||
switch (evt.type) {
|
||||
case 'mousedown':
|
||||
mouseData.start = evt.clientY;
|
||||
break;
|
||||
case ' mousemove':
|
||||
if (mouseData.start) {
|
||||
mouseData.moveDistance = evt.clientY - mouseData.start;
|
||||
const conHeight = canvas.parentElement.offsetHeight;
|
||||
const targetSigma = (mouseData.moveDistance / conHeight) * 10;
|
||||
// 不会有模糊渐变
|
||||
canvas.showImmediately(targetSigma);
|
||||
}
|
||||
break;
|
||||
case 'mouseup':
|
||||
mouseData.start = mouseData.moveDistance = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
|
@ -0,0 +1,287 @@
|
|||
import {html, css, LitElement} from 'lit'
|
||||
import {customElement, property, query} from 'lit/decorators.js'
|
||||
import workerUrl from './worker'
|
||||
enum ErrorType {
|
||||
IMAGE_LOAD_FAILED = 'Image load failed!',
|
||||
IMAGE_NOT_PREPARED = 'The Image resource is not completely prepared!',
|
||||
IMAGE_CROS_ERROR = 'The Image resource crosses domain!',
|
||||
CANVAS_PUTIMAGEDATA_ERROR = 'Canvas putImageData failed!',
|
||||
}
|
||||
|
||||
const cubic_bezier = (
|
||||
p0: number,
|
||||
p1: number,
|
||||
p2: number,
|
||||
p3: number,
|
||||
t: number
|
||||
): number => {
|
||||
return (
|
||||
p0 * Math.pow(1 - t, 3) +
|
||||
3 * p1 * t * Math.pow(1 - t, 2) +
|
||||
3 * p2 * t * t * (1 - t) +
|
||||
p3 * Math.pow(t, 3)
|
||||
)
|
||||
}
|
||||
|
||||
@customElement('gauss-canvas')
|
||||
export default class GaussCanvas extends LitElement {
|
||||
@query('#display') display!: HTMLCanvasElement
|
||||
@property({type: String})
|
||||
get src() {
|
||||
return this._src
|
||||
}
|
||||
set src(value) {
|
||||
if (value && value !== this._src) {
|
||||
const loadCb = () => {
|
||||
this._src = value
|
||||
}
|
||||
const errCb = () => {
|
||||
this._img.src = this._src
|
||||
this._img.removeEventListener('load', loadCb)
|
||||
}
|
||||
|
||||
this._img.addEventListener('error', errCb, {once: true})
|
||||
this._img.addEventListener('load', loadCb, {once: true})
|
||||
|
||||
this._img.src = value
|
||||
}
|
||||
}
|
||||
|
||||
@property() threshold: number = 1
|
||||
@property() transition: boolean = false
|
||||
|
||||
@property()
|
||||
get sigma() {
|
||||
return this._sigma
|
||||
}
|
||||
set sigma(value) {
|
||||
if (value < 0) {
|
||||
value = 0
|
||||
}
|
||||
if (this._sigma == -1) {
|
||||
this._sigma = Number(value)
|
||||
this.show(value)
|
||||
} else if (value !== this._sigma && this._targetSigma == -1) {
|
||||
this._targetSigma = value
|
||||
this.openAnimation()
|
||||
} else {
|
||||
console.info('changed target sigma')
|
||||
}
|
||||
}
|
||||
|
||||
@property({type: Number}) bezier: [number, number, number, number] = [
|
||||
0.19, 1, 0.22, 1,
|
||||
]
|
||||
@property() during = 500
|
||||
|
||||
readonly _img: HTMLImageElement = document.createElement('img')
|
||||
_loadStatus: boolean = false
|
||||
_imgWidth: number = 0
|
||||
_imgHeight: number = 0
|
||||
|
||||
readonly _trCanvas: HTMLCanvasElement = document.createElement('canvas')
|
||||
readonly _trCtx = this._trCanvas.getContext('2d')!
|
||||
_imageData: ImageData | undefined
|
||||
|
||||
_src: string = ''
|
||||
_ctx!: CanvasRenderingContext2D
|
||||
_sigma: number = -1
|
||||
_targetSigma: number = -1
|
||||
|
||||
_worker: Worker | undefined
|
||||
_id!: number
|
||||
|
||||
disWidth: number = 0
|
||||
disHeight: number = 0
|
||||
|
||||
constructor(url: string) {
|
||||
super()
|
||||
this._img.addEventListener('error', this.loadError)
|
||||
this._img.addEventListener('load', this.loadSuccess)
|
||||
this._id = new Date().getTime()
|
||||
this.src = url
|
||||
}
|
||||
|
||||
loadError = () => {
|
||||
this._loadStatus = false
|
||||
this._imgWidth = this._imgHeight = 0
|
||||
|
||||
console.error(ErrorType.IMAGE_LOAD_FAILED)
|
||||
}
|
||||
|
||||
loadSuccess = () => {
|
||||
this._loadStatus = true
|
||||
this._imgHeight = this._img.height
|
||||
this._imgWidth = this._img.width
|
||||
this.show(this.sigma)
|
||||
}
|
||||
|
||||
drawWorker = (
|
||||
sigma: number,
|
||||
imageData: ImageData,
|
||||
canvas: HTMLCanvasElement,
|
||||
draw: Function
|
||||
) => {
|
||||
if (sigma > this.threshold) {
|
||||
if (!this._worker) {
|
||||
this._worker = new Worker(workerUrl)
|
||||
this._worker.addEventListener('message', (evt) => {
|
||||
draw(evt.data.data, evt.data.id)
|
||||
})
|
||||
}
|
||||
|
||||
this._worker.postMessage({
|
||||
imageData,
|
||||
sigma,
|
||||
id: this._id,
|
||||
height: canvas.height,
|
||||
width: canvas.width,
|
||||
})
|
||||
this.isFormatting = true
|
||||
} else {
|
||||
draw(imageData, this._id)
|
||||
this.cb?.()
|
||||
}
|
||||
}
|
||||
|
||||
cb: Function | undefined
|
||||
isFormatting: boolean = false
|
||||
show = (sigma: number, cb?: Function) => {
|
||||
this.cb = cb
|
||||
return new Promise((res, rej) => {
|
||||
if (!this._loadStatus) {
|
||||
if (!this._img.src.includes(this._src)) {
|
||||
this._img.src = this._src
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (this.isFormatting) {
|
||||
return res(void 0)
|
||||
}
|
||||
let ratio: number = sigma
|
||||
if (sigma == 0) {
|
||||
ratio = 1
|
||||
} else if (sigma < this.threshold) {
|
||||
ratio = 2 * this.threshold - 1
|
||||
}
|
||||
|
||||
const canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')!
|
||||
canvas.height = this._img.height / ratio
|
||||
canvas.width = this._img.width / ratio
|
||||
|
||||
ctx.drawImage(
|
||||
this._img,
|
||||
0,
|
||||
0,
|
||||
this._img.width,
|
||||
this._img.height,
|
||||
0,
|
||||
0,
|
||||
canvas.width,
|
||||
canvas.height
|
||||
)
|
||||
|
||||
const draw = (imageData: ImageData, dataId: number) => {
|
||||
if (dataId == this._id) {
|
||||
if (this.cb) {
|
||||
this.cb?.()
|
||||
}
|
||||
// +100 为了清除拖影
|
||||
this.disHeight = this.display.height = imageData.height /* + 100 */
|
||||
this.disWidth = this.display.width = imageData.width
|
||||
this._ctx.putImageData(imageData, 0, 0)
|
||||
this.isFormatting = false
|
||||
res(void 0)
|
||||
}
|
||||
}
|
||||
try {
|
||||
let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
|
||||
this.drawWorker(sigma, imageData, canvas, draw)
|
||||
} catch (error: any) {
|
||||
if (error.code == 18) {
|
||||
return rej(ErrorType.IMAGE_CROS_ERROR)
|
||||
}
|
||||
return rej(ErrorType.CANVAS_PUTIMAGEDATA_ERROR)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
showImmediately(sigma: number) {
|
||||
return new Promise((res) => {
|
||||
this._sigma = sigma
|
||||
this.show(sigma).then(res)
|
||||
})
|
||||
}
|
||||
|
||||
_startTime!: number
|
||||
animation: Function | undefined
|
||||
openAnimation() {
|
||||
this._startTime = new Date().getTime()
|
||||
const getCurSigma = () => {
|
||||
const t = (new Date().getTime() - this._startTime) / this.during
|
||||
let ratio = cubic_bezier(...this.bezier, t)
|
||||
|
||||
if (ratio > 0.95) {
|
||||
return this._targetSigma
|
||||
}
|
||||
const result = (this._targetSigma - this._sigma) * ratio + this._sigma
|
||||
|
||||
return result > 0 ? result : 0
|
||||
}
|
||||
let changeSigma = () => {
|
||||
this._sigma = getCurSigma()
|
||||
|
||||
if (this._targetSigma !== this._sigma) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
this.animation = () => {
|
||||
if (changeSigma()) {
|
||||
// console.time(`sigma: ${this._sigma}`)
|
||||
this.show(this._sigma, () => {
|
||||
// console.timeEnd(`sigma: ${this._sigma}`)
|
||||
// @ts-ignore
|
||||
requestAnimationFrame(this.animation!)
|
||||
})
|
||||
} else {
|
||||
this._targetSigma = -1
|
||||
}
|
||||
}
|
||||
|
||||
this.animation()
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
this._ctx = this.display.getContext('2d')!
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<canvas id="display"></canvas>
|
||||
`
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
margin: auto;
|
||||
height: calc(100%);
|
||||
width: 100%;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'gauss-canvas': GaussCanvas
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "@star-web-components/gauss",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"main": "./index.js",
|
||||
"module": "./index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./index": {
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./index.js": {
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./icons/index.js": {
|
||||
"default": "./icons/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
// import {
|
||||
// cubic_bezier,
|
||||
// fastBlur,
|
||||
// genSrcAndDest,
|
||||
// mergeChannels,
|
||||
// genKernelsForGaussian,
|
||||
// _fastBlur,
|
||||
// hFastMotionBlur,
|
||||
// vFastMotionBlur,
|
||||
// } from './utils'
|
||||
// @ts-ignore
|
||||
const workCode = () => {
|
||||
function genKernelsForGaussian(sigma: number, n: number) {
|
||||
const wIdeal = Math.sqrt((12 * Math.pow(sigma, 2)) / n + 1)
|
||||
const sizes = []
|
||||
let wl = Math.floor(wIdeal)
|
||||
|
||||
if (wl % 2 === 0) {
|
||||
wl--
|
||||
}
|
||||
const wu = wl + 2
|
||||
let m =
|
||||
(12 * Math.pow(sigma, 2) - n * Math.pow(wl, 2) - 4 * n * wl - 3 * n) /
|
||||
(-4 * wl - 4)
|
||||
m = Math.round(m)
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
sizes.push(i < m ? wl : wu)
|
||||
}
|
||||
|
||||
return sizes
|
||||
}
|
||||
|
||||
function genSrcAndDest(data: Uint8ClampedArray) {
|
||||
const dataInt8 = new Uint8ClampedArray(data.buffer)
|
||||
const simpleChannelLength = dataInt8.length / 4
|
||||
const r = new Uint8ClampedArray(simpleChannelLength)
|
||||
const g = new Uint8ClampedArray(simpleChannelLength)
|
||||
const b = new Uint8ClampedArray(simpleChannelLength)
|
||||
const a = new Uint8ClampedArray(simpleChannelLength)
|
||||
const _r = new Uint8ClampedArray(simpleChannelLength)
|
||||
const _g = new Uint8ClampedArray(simpleChannelLength)
|
||||
const _b = new Uint8ClampedArray(simpleChannelLength)
|
||||
const _a = new Uint8ClampedArray(simpleChannelLength)
|
||||
for (let i = 0; i < simpleChannelLength; i++) {
|
||||
_r[i] = r[i] = dataInt8[i * 4]
|
||||
_g[i] = g[i] = dataInt8[i * 4 + 1]
|
||||
_b[i] = b[i] = dataInt8[i * 4 + 2]
|
||||
_a[i] = a[i] = dataInt8[i * 4 + 3]
|
||||
}
|
||||
return {src: [r, g, b, a], dest: [_r, _g, _b, _a]}
|
||||
}
|
||||
|
||||
function mergeChannels([r, g, b, a]: Uint8ClampedArray[]) {
|
||||
const simpleChannelLength = r.length
|
||||
const data = new Uint8ClampedArray(simpleChannelLength * 4)
|
||||
for (let i = 0; i < simpleChannelLength; i++) {
|
||||
data[4 * i] = r[i]
|
||||
data[4 * i + 1] = g[i]
|
||||
data[4 * i + 2] = b[i]
|
||||
data[4 * i + 3] = a[i]
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
function hFastMotionBlur(
|
||||
src: Uint8ClampedArray,
|
||||
dest: Uint8ClampedArray,
|
||||
width: number,
|
||||
height: number,
|
||||
radius: number
|
||||
) {
|
||||
for (let i = 0; i < height; i++) {
|
||||
let accumulation = radius * src[i * width]
|
||||
for (let j = 0; j <= radius; j++) {
|
||||
accumulation += src[i * width + j]
|
||||
}
|
||||
|
||||
dest[i * width] = Math.round(accumulation / (2 * radius + 1))
|
||||
|
||||
for (let j = 1; j < width; j++) {
|
||||
const left = Math.max(0, j - radius - 1)
|
||||
const right = Math.min(width - 1, j + radius)
|
||||
accumulation =
|
||||
accumulation + (src[i * width + right] - src[i * width + left])
|
||||
dest[i * width + j] = Math.round(accumulation / (2 * radius + 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function vFastMotionBlur(
|
||||
src: Uint8ClampedArray,
|
||||
dest: Uint8ClampedArray,
|
||||
width: number,
|
||||
height: number,
|
||||
radius: number
|
||||
) {
|
||||
for (let i = 0; i < width; i++) {
|
||||
let accumulation = radius * src[i]
|
||||
for (let j = 0; j <= radius; j++) {
|
||||
accumulation += src[j * width + i]
|
||||
}
|
||||
|
||||
dest[i] = Math.round(accumulation / (2 * radius + 1))
|
||||
|
||||
for (let j = 1; j < height; j++) {
|
||||
const top = Math.max(0, j - radius - 1)
|
||||
const bottom = Math.min(height - 1, j + radius)
|
||||
accumulation =
|
||||
accumulation + src[bottom * width + i] - src[top * width + i]
|
||||
dest[j * width + i] = Math.round(accumulation / (2 * radius + 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _fastBlur(
|
||||
src: Uint8ClampedArray,
|
||||
dest: Uint8ClampedArray,
|
||||
width: number,
|
||||
height: number,
|
||||
radius: number
|
||||
) {
|
||||
hFastMotionBlur(dest, src, width, height, radius)
|
||||
vFastMotionBlur(src, dest, width, height, radius)
|
||||
}
|
||||
|
||||
function fastBlur(
|
||||
src: Uint8ClampedArray,
|
||||
dest: Uint8ClampedArray,
|
||||
width: number,
|
||||
height: number,
|
||||
sigma: number
|
||||
) {
|
||||
const boxes = genKernelsForGaussian(sigma, 3)
|
||||
|
||||
for (let i = 0; i < src.length; i++) {
|
||||
dest[i] = src[i]
|
||||
}
|
||||
|
||||
_fastBlur(src, dest, width, height, (boxes[0] - 1) / 2)
|
||||
_fastBlur(src, dest, width, height, (boxes[1] - 1) / 2)
|
||||
_fastBlur(src, dest, width, height, (boxes[2] - 1) / 2)
|
||||
|
||||
return dest
|
||||
}
|
||||
|
||||
onmessage = (evt) => {
|
||||
postMessage({data: blurCanvas(evt.data), id: evt.data.id})
|
||||
}
|
||||
let blurCanvas = ({
|
||||
imageData,
|
||||
height,
|
||||
width,
|
||||
sigma,
|
||||
}: {
|
||||
imageData: ImageData
|
||||
sigma: number
|
||||
height: number
|
||||
width: number
|
||||
}) => {
|
||||
const {src: srcRgba, dest: destRgba} = genSrcAndDest(imageData.data)
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
fastBlur(srcRgba[i], destRgba[i], width, height, sigma)
|
||||
}
|
||||
const destData = mergeChannels(destRgba)
|
||||
imageData.data.set(destData)
|
||||
return imageData
|
||||
}
|
||||
}
|
||||
|
||||
const transfer = () => {
|
||||
let codeStr: string = ''
|
||||
|
||||
codeStr += `(${workCode.toString()})()`
|
||||
return codeStr
|
||||
}
|
||||
|
||||
let workBlob = new Blob([transfer()])
|
||||
|
||||
export default URL.createObjectURL(workBlob)
|
|
@ -1,5 +1,5 @@
|
|||
import {html, LitElement, css} from 'lit'
|
||||
import {customElement, property} from 'lit/decorators.js'
|
||||
import {customElement} from 'lit/decorators.js'
|
||||
import '../../../components/button/button'
|
||||
import '../../../components/overlay/active-overlay'
|
||||
import {OverlayStack} from '../../../components/overlay/overlay-stack'
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
import {html, css, LitElement, CSSResultGroup} from 'lit'
|
||||
import {customElement, property, query} from 'lit/decorators.js'
|
||||
import '../../../components/gauss_canvas/index'
|
||||
import GaussCanvas from '../../../components/gauss_canvas/index'
|
||||
|
||||
@customElement('panel-gauss')
|
||||
export class GaussBlur extends LitElement {
|
||||
@query('#container') container!: HTMLDivElement
|
||||
@query('gauss-canvas') canvas!: GaussCanvas
|
||||
// _src = '/src/test/panels/gauss_canvas/big.jpeg'
|
||||
@property() _src!: string
|
||||
// 'https://fanyiapp.cdn.bcebos.com/cms/image/cfacf96e5beb2a8444e016b96fb96ab6.jpg'
|
||||
@property({type: Number}) sigma: number = 2
|
||||
@property({type: Number}) topPositionSigma: number = 20
|
||||
@property({type: Number}) bottomPositionSigma: number = 2
|
||||
|
||||
_moveFlag: boolean = false
|
||||
_offsetY: number = 0
|
||||
_start: number = 0
|
||||
_moveDistance: number = 0
|
||||
set offsetY(value: number) {
|
||||
this._offsetY = -value
|
||||
this.canvas.display.style.transform = `translateY(${value}px)`
|
||||
}
|
||||
|
||||
handleEvent = (evt: TouchEvent | MouseEvent) => {
|
||||
switch (evt.type) {
|
||||
case 'touchstart':
|
||||
case 'mousedown':
|
||||
this._moveFlag = false
|
||||
if (evt instanceof MouseEvent) {
|
||||
this._start = evt.clientY
|
||||
} else {
|
||||
this._start = evt.touches[0].pageY
|
||||
}
|
||||
break
|
||||
case 'touchmove':
|
||||
case 'mousemove':
|
||||
if (this._start) {
|
||||
if (evt instanceof MouseEvent) {
|
||||
this._moveDistance = evt.clientY - this._start
|
||||
} else {
|
||||
this._moveDistance = evt.touches[0].pageY - this._start
|
||||
}
|
||||
|
||||
if (this._moveDistance > 0) this._moveDistance = 0
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
this.offsetY = this._moveDistance
|
||||
this.changeSigma()
|
||||
})
|
||||
}
|
||||
this._moveFlag = true
|
||||
|
||||
break
|
||||
case 'touchend':
|
||||
case 'mouseup':
|
||||
this._start = this._moveDistance = 0
|
||||
break
|
||||
case 'click':
|
||||
!this._moveFlag && (this.canvas.sigma ^= 10)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* change sigma fowllowing this.offsetY
|
||||
*/
|
||||
changeSigma() {
|
||||
const conHeight = this.container.offsetHeight
|
||||
const targetSigma =
|
||||
(this._offsetY / conHeight) *
|
||||
(this.topPositionSigma - this.bottomPositionSigma) +
|
||||
this.bottomPositionSigma
|
||||
this.canvas.showImmediately(targetSigma)
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
;(window as any).panel = this
|
||||
|
||||
this.addEventListener('touchstart', this)
|
||||
this.addEventListener('touchmove', this)
|
||||
this.addEventListener('touchend', this)
|
||||
this.addEventListener('mousedown', this)
|
||||
this.addEventListener('mousemove', this)
|
||||
this.addEventListener('mouseup', this)
|
||||
this.canvas.addEventListener('click', this)
|
||||
}
|
||||
handleInputFile(evt: Event) {
|
||||
const imgfile = (evt.target as HTMLInputElement).files?.[0]
|
||||
if (imgfile) {
|
||||
this._src = URL.createObjectURL(imgfile)
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return html`
|
||||
<div id="container">
|
||||
<input type="file" @change=${this.handleInputFile} />
|
||||
<gauss-canvas src=${this._src} sigma=${this.sigma}></gauss-canvas>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
static styles?: CSSResultGroup | undefined = css`
|
||||
#container {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
will-change: transform;
|
||||
overflow: hidden;
|
||||
}
|
||||
input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
gauss-canvas {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'panel-gauss': GaussBlur
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import {html, LitElement, css, CSSResultArray} from 'lit'
|
||||
import {customElement, property} from 'lit/decorators.js'
|
||||
import {customElement} from 'lit/decorators.js'
|
||||
import '../../../components/button/button'
|
||||
import '../../../components/ul/ul'
|
||||
import '../../../components/li//li'
|
||||
|
|
|
@ -14,6 +14,7 @@ import './card/card'
|
|||
import './indicators/indicators'
|
||||
import './indicators/home-indicator'
|
||||
import './blur/use-blur'
|
||||
import './gauss_canvas/gauss-blur'
|
||||
import './button/button'
|
||||
import './container/container'
|
||||
import './radio/radio'
|
||||
|
@ -258,6 +259,14 @@ export class PanelRoot extends LitElement {
|
|||
href="#blur"
|
||||
></star-li>
|
||||
<hr />
|
||||
<star-li
|
||||
type=${LiType.ICON_LABEL}
|
||||
label="高斯模糊"
|
||||
icon="achievement"
|
||||
iconcolor="gold"
|
||||
href="#gauss"
|
||||
></star-li>
|
||||
<hr />
|
||||
<star-li
|
||||
type=${LiType.ICON_LABEL}
|
||||
label="主屏"
|
||||
|
|
Loading…
Reference in New Issue