|
@@ -0,0 +1,206 @@
|
|
|
+import { ref, onScopeDispose, watch, nextTick, unref, computed, watchEffect } from 'vue'
|
|
|
+import type { Ref } from 'vue'
|
|
|
+import { isDom } from '@/utils/common'
|
|
|
+import TintImageWorker from '@/utils/image.worker?worker'
|
|
|
+import useMainStore from '@/store/main'
|
|
|
+import { useRoute } from 'vue-router'
|
|
|
+import analyze from 'rgbaster'
|
|
|
+export type RGBA = number[]
|
|
|
+
|
|
|
+export type Point = [number, number]
|
|
|
+export interface SetImgBgOption {
|
|
|
+ image?: string | HTMLImageElement | null
|
|
|
+ basePoint?: Point[]
|
|
|
+ distance?: number
|
|
|
+ enableSharpen?: boolean
|
|
|
+ rotate?: number
|
|
|
+ scale?: number
|
|
|
+}
|
|
|
+
|
|
|
+interface InitOption {
|
|
|
+ frontColor?: Ref<string>
|
|
|
+ setFrontColor?: any
|
|
|
+}
|
|
|
+
|
|
|
+function getRgba(canvas: any, that: any) {
|
|
|
+ const imgWidth = that.width
|
|
|
+ const imgHeight = that.height
|
|
|
+
|
|
|
+ canvas.width = imgWidth
|
|
|
+ canvas.height = imgHeight
|
|
|
+
|
|
|
+ const context = canvas.getContext('2d')
|
|
|
+
|
|
|
+ context.drawImage(that, 0, 0, imgWidth, imgHeight)
|
|
|
+ const imgdatas = context.getImageData(0, 0, imgWidth, imgHeight)
|
|
|
+ const imgdata = imgdatas.data
|
|
|
+ const newJson: any = {}
|
|
|
+ const length = imgdata.length
|
|
|
+ console.log('length:' + length)
|
|
|
+ for (let i = 0; i < 250; i++) {
|
|
|
+ if (i % 4 === 0) {
|
|
|
+ const alpha = Math.round((imgdata[i + 3] / 255) * 100) / 100
|
|
|
+ const rgba = imgdata[i] + ',' + imgdata[i + 1] + ',' + imgdata[i + 2] + ',' + alpha
|
|
|
+ if (!newJson[rgba]) {
|
|
|
+ newJson[rgba] = 1
|
|
|
+ } else {
|
|
|
+ newJson[rgba]++
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let maxNum = 0
|
|
|
+ let maxVal = ''
|
|
|
+ for (const key in newJson) {
|
|
|
+ if (newJson[key] > maxNum) {
|
|
|
+ maxNum = newJson[key]
|
|
|
+ maxVal = key
|
|
|
+ }
|
|
|
+ }
|
|
|
+ console.log('rgba:', maxVal + ';次数:' + maxNum)
|
|
|
+ return maxVal
|
|
|
+}
|
|
|
+function getHex(...value: any) {
|
|
|
+ const r = value[0].toString(16)
|
|
|
+ const g = value[1].toString(16)
|
|
|
+ const b = value[2].toString(16)
|
|
|
+ let hex = r + g + b
|
|
|
+ if (r.slice(0, 1) == r.slice(1, 1) && g.slice(0, 1) == g.slice(1, 1) && b.slice(0, 1) == b.slice(1, 1)) {
|
|
|
+ hex = r.slice(0, 1) + g.slice(0, 1) + b.slice(0, 1)
|
|
|
+ }
|
|
|
+ return hex
|
|
|
+}
|
|
|
+
|
|
|
+interface MessageData {
|
|
|
+ drawing: boolean
|
|
|
+ blob?: Blob
|
|
|
+}
|
|
|
+
|
|
|
+export const usePreloadImageToTask = (
|
|
|
+ option: Ref<SetImgBgOption>,
|
|
|
+ frontColor?: Ref<string>,
|
|
|
+ setFrontColor?: any,
|
|
|
+ task?: any
|
|
|
+) => {
|
|
|
+ const initImage = async ({ task, frontColor, setFrontColor }: any) => {
|
|
|
+ if (!task.url) {
|
|
|
+ return console.warn(`task缺少url属性:`, task)
|
|
|
+ }
|
|
|
+
|
|
|
+ const image = await new Promise<HTMLImageElement>((resolve, reject) => {
|
|
|
+ const img = new Image()
|
|
|
+ img.src = task.url as string
|
|
|
+ img.onload = async () => {
|
|
|
+ if (frontColor && setFrontColor) {
|
|
|
+ try {
|
|
|
+ const result = await analyze(task.url, { scale: 0.2 })
|
|
|
+ const bgColorRgb = result[0].color
|
|
|
+ const bgColorNum = bgColorRgb.split('rgb(')[1].slice(0, -1)
|
|
|
+ const splitArr = bgColorNum.split(',')
|
|
|
+ const hex = getHex(Number(splitArr[0]), Number(splitArr[1]), Number(splitArr[2]))
|
|
|
+ const { fullPath } = useRoute()
|
|
|
+ const mainStore = useMainStore()
|
|
|
+ const userSetColor = mainStore.userMarkConfig?.[fullPath]?.frontColor
|
|
|
+ if (!userSetColor) {
|
|
|
+ setFrontColor('#' + hex)
|
|
|
+ }
|
|
|
+ } catch {}
|
|
|
+ }
|
|
|
+
|
|
|
+ resolve(img)
|
|
|
+ }
|
|
|
+ img.onerror = () => {
|
|
|
+ console.log('img error')
|
|
|
+ reject()
|
|
|
+ }
|
|
|
+ img.onabort = () => {
|
|
|
+ console.log('img aborted')
|
|
|
+ reject()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return image
|
|
|
+ }
|
|
|
+
|
|
|
+ const imageWorker = new TintImageWorker()
|
|
|
+ const drawing = ref<boolean>(false)
|
|
|
+ const dataUrl = ref('')
|
|
|
+ imageWorker.addEventListener('message', (e) => {
|
|
|
+ const data = e.data as MessageData
|
|
|
+ drawing.value = data.drawing
|
|
|
+ dataUrl.value = data.blob ? URL.createObjectURL(data.blob) : ''
|
|
|
+ task.dataUrl = dataUrl.value
|
|
|
+ })
|
|
|
+
|
|
|
+ nextTick(() => {
|
|
|
+ const opt = unref(option)
|
|
|
+ if (!opt.image) {
|
|
|
+ drawing.value = false
|
|
|
+ dataUrl.value = ''
|
|
|
+ return
|
|
|
+ }
|
|
|
+ initImage({ task, frontColor, setFrontColor })
|
|
|
+ .then((image) => {
|
|
|
+ if (!image) return
|
|
|
+ createImageBitmap(image).then((imageBitMap) => {
|
|
|
+ imageWorker.postMessage(
|
|
|
+ {
|
|
|
+ type: 'init',
|
|
|
+ imageBitMap,
|
|
|
+ basePoint: opt.basePoint,
|
|
|
+ enableSharpen: opt.enableSharpen,
|
|
|
+ distance: opt.distance,
|
|
|
+ scale: opt.scale,
|
|
|
+ rotate: opt.rotate,
|
|
|
+ },
|
|
|
+ [imageBitMap]
|
|
|
+ )
|
|
|
+ })
|
|
|
+ })
|
|
|
+ .catch((err: any) => {
|
|
|
+ drawing.value = false
|
|
|
+ dataUrl.value = ''
|
|
|
+ console.log('initImage catch err')
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ watch(
|
|
|
+ () => option.value.scale,
|
|
|
+ () => {
|
|
|
+ imageWorker.postMessage({ type: 'scale', scale: option.value?.scale })
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+ watch(
|
|
|
+ () => option.value.rotate,
|
|
|
+ () => {
|
|
|
+ imageWorker.postMessage({ type: 'rotate', rotate: option.value?.rotate })
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+ watch(dataUrl, (current, prev) => {
|
|
|
+ const imgWrapDom = document.querySelector('.img-wrap')
|
|
|
+ if (imgWrapDom) {
|
|
|
+ setTimeout(() => {
|
|
|
+ imgWrapDom.scrollTop = 0
|
|
|
+ }, 10)
|
|
|
+ }
|
|
|
+ if (prev) {
|
|
|
+ try {
|
|
|
+ URL.revokeObjectURL(prev)
|
|
|
+ } catch (error) {}
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ onScopeDispose(() => {
|
|
|
+ if (dataUrl.value) {
|
|
|
+ try {
|
|
|
+ URL.revokeObjectURL(dataUrl.value)
|
|
|
+ } catch (error) {}
|
|
|
+ }
|
|
|
+ imageWorker.terminate()
|
|
|
+ })
|
|
|
+
|
|
|
+ return {
|
|
|
+ drawing,
|
|
|
+ dataUrl,
|
|
|
+ }
|
|
|
+}
|