useSetImgBg.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import { ref, onScopeDispose, watch, nextTick, unref, computed, watchEffect } from 'vue'
  2. import type { Ref } from 'vue'
  3. import { isDom } from '@/utils/common'
  4. import TintImageWorker from '@/utils/image.worker?worker'
  5. import useMainStore from '@/store/main'
  6. import { useRoute } from 'vue-router'
  7. import analyze from 'rgbaster'
  8. export type RGBA = number[]
  9. export type Point = [number, number]
  10. export interface SetImgBgOption {
  11. image?: string | HTMLImageElement | null
  12. basePoint?: Point[]
  13. distance?: number
  14. enableSharpen?: boolean
  15. rotate?: number
  16. scale?: number
  17. }
  18. interface InitOption {
  19. image: string | HTMLImageElement
  20. frontColor?: Ref<string>
  21. setFrontColor?: any
  22. }
  23. function getRgba(canvas: any, that: any) {
  24. const imgWidth = that.width
  25. const imgHeight = that.height
  26. canvas.width = imgWidth
  27. canvas.height = imgHeight
  28. const context = canvas.getContext('2d')
  29. context.drawImage(that, 0, 0, imgWidth, imgHeight)
  30. const imgdatas = context.getImageData(0, 0, imgWidth, imgHeight)
  31. const imgdata = imgdatas.data
  32. const newJson: any = {}
  33. const length = imgdata.length
  34. console.log('length:' + length)
  35. for (let i = 0; i < 250; i++) {
  36. if (i % 4 === 0) {
  37. const alpha = Math.round((imgdata[i + 3] / 255) * 100) / 100
  38. const rgba = imgdata[i] + ',' + imgdata[i + 1] + ',' + imgdata[i + 2] + ',' + alpha
  39. if (!newJson[rgba]) {
  40. newJson[rgba] = 1
  41. } else {
  42. newJson[rgba]++
  43. }
  44. }
  45. }
  46. let maxNum = 0
  47. let maxVal = ''
  48. for (const key in newJson) {
  49. if (newJson[key] > maxNum) {
  50. maxNum = newJson[key]
  51. maxVal = key
  52. }
  53. }
  54. console.log('rgba:', maxVal + ';次数:' + maxNum)
  55. return maxVal
  56. }
  57. function getHex(...value: any) {
  58. const r = value[0].toString(16)
  59. const g = value[1].toString(16)
  60. const b = value[2].toString(16)
  61. let hex = r + g + b
  62. 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)) {
  63. hex = r.slice(0, 1) + g.slice(0, 1) + b.slice(0, 1)
  64. }
  65. return hex
  66. }
  67. interface MessageData {
  68. drawing: boolean
  69. blob?: Blob
  70. }
  71. export const useSetImgBg = (option: Ref<SetImgBgOption>, frontColor?: Ref<string>, setFrontColor?: any) => {
  72. const { fullPath } = useRoute()
  73. const mainStore = useMainStore()
  74. const userSetColor = computed(() => {
  75. return mainStore.userMarkConfig?.[fullPath]?.frontColor
  76. })
  77. watchEffect(() => {
  78. if (!!userSetColor.value && setFrontColor) {
  79. setFrontColor(userSetColor.value)
  80. }
  81. })
  82. const initImage = async ({ image, frontColor, setFrontColor }: InitOption) => {
  83. if (!image) {
  84. return console.warn(`return for img define ${image}`)
  85. }
  86. if (!isDom<HTMLImageElement>(image)) {
  87. image = await new Promise<HTMLImageElement>((resolve, reject) => {
  88. const img = new Image()
  89. img.src = image as string
  90. img.onload = async () => {
  91. if (frontColor && setFrontColor) {
  92. // console.time()
  93. try {
  94. const result = await analyze(image, { scale: 0.2 })
  95. const bgColorRgb = result[0].color
  96. const bgColorNum = bgColorRgb.split('rgb(')[1].slice(0, -1)
  97. const splitArr = bgColorNum.split(',')
  98. const hex = getHex(Number(splitArr[0]), Number(splitArr[1]), Number(splitArr[2]))
  99. if (!userSetColor?.value) {
  100. setFrontColor('#' + hex)
  101. }
  102. } catch {}
  103. // console.timeEnd()
  104. // const canvas = document.createElement('canvas')
  105. // const maxVal = getRgba(canvas, this)
  106. // console.log('maxVal:', maxVal)
  107. // const splitArr = maxVal.split(',')
  108. // const hex = getHex(Number(splitArr[0]), Number(splitArr[1]), Number(splitArr[2]))
  109. // if (!userSetColor?.value) {
  110. // setFrontColor('#' + hex)
  111. // }
  112. }
  113. resolve(img)
  114. }
  115. img.onerror = () => {
  116. console.log('img error')
  117. reject()
  118. }
  119. img.onabort = () => {
  120. console.log('img aborted')
  121. reject()
  122. }
  123. })
  124. }
  125. return image
  126. }
  127. const imageWorker = new TintImageWorker()
  128. const drawing = ref<boolean>(false)
  129. const dataUrl = ref('')
  130. imageWorker.addEventListener('message', (e) => {
  131. const data = e.data as MessageData
  132. drawing.value = data.drawing
  133. dataUrl.value = data.blob ? URL.createObjectURL(data.blob) : ''
  134. })
  135. watch(
  136. [
  137. () => option.value?.image,
  138. () => option.value?.distance,
  139. () => option.value?.basePoint,
  140. () => option.value?.enableSharpen,
  141. ],
  142. () => {
  143. nextTick(() => {
  144. const opt = unref(option)
  145. if (!opt.image) {
  146. drawing.value = false
  147. dataUrl.value = ''
  148. return
  149. }
  150. initImage({ image: opt.image, frontColor, setFrontColor })
  151. .then((image) => {
  152. if (!image) return
  153. createImageBitmap(image).then((imageBitMap) => {
  154. imageWorker.postMessage(
  155. {
  156. type: 'init',
  157. imageBitMap,
  158. basePoint: opt.basePoint,
  159. enableSharpen: opt.enableSharpen,
  160. distance: opt.distance,
  161. scale: opt.scale,
  162. rotate: opt.rotate,
  163. },
  164. [imageBitMap]
  165. )
  166. })
  167. })
  168. .catch((err: any) => {
  169. drawing.value = false
  170. dataUrl.value = ''
  171. console.log('initImage catch err')
  172. })
  173. })
  174. },
  175. { immediate: true }
  176. )
  177. watch(
  178. () => option.value.scale,
  179. () => {
  180. imageWorker.postMessage({ type: 'scale', scale: option.value?.scale })
  181. }
  182. )
  183. watch(
  184. () => option.value.rotate,
  185. () => {
  186. imageWorker.postMessage({ type: 'rotate', rotate: option.value?.rotate })
  187. }
  188. )
  189. watch(dataUrl, (current, prev) => {
  190. const imgWrapDom = document.querySelector('.img-wrap')
  191. if (imgWrapDom) {
  192. setTimeout(() => {
  193. imgWrapDom.scrollTop = 0
  194. }, 10)
  195. }
  196. if (prev) {
  197. try {
  198. URL.revokeObjectURL(prev)
  199. } catch (error) {}
  200. }
  201. })
  202. onScopeDispose(() => {
  203. if (dataUrl.value) {
  204. try {
  205. URL.revokeObjectURL(dataUrl.value)
  206. } catch (error) {}
  207. }
  208. imageWorker.terminate()
  209. })
  210. return {
  211. drawing,
  212. dataUrl,
  213. }
  214. }