Skip to content

Commit 8fbfa66

Browse files
committed
feat: add blur hash
Signed-off-by: Innei <[email protected]>
1 parent 8a4a30f commit 8fbfa66

File tree

5 files changed

+77
-6
lines changed

5 files changed

+77
-6
lines changed

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"@xterm/addon-fit": "0.10.0",
5757
"@xterm/xterm": "5.5.0",
5858
"ansi_up": "6.0.2",
59+
"blurhash": "2.0.5",
5960
"buffer": "6.0.3",
6061
"canvas-confetti": "1.9.3",
6162
"class-transformer": "0.5.1",
@@ -127,5 +128,6 @@
127128
"vite-plugin-wasm": "3.3.0",
128129
"vite-tsconfig-paths": "4.3.2",
129130
"windicss": "3.5.6"
130-
}
131-
}
131+
},
132+
"packageManager": "[email protected]"
133+
}

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/drawer/components/image-detail-section.tsx

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { decode } from 'blurhash'
12
import { uniqBy } from 'lodash-es'
23
import {
34
NButton,
@@ -10,11 +11,12 @@ import {
1011
NInput,
1112
NInputNumber,
1213
} from 'naive-ui'
13-
import { getDominantColor } from '~/utils/image'
14-
import { isVideoExt, pickImagesFromMarkdown } from '~/utils/markdown'
1514
import type { Image as ImageModel } from '~/models/base'
1615
import type { PropType } from 'vue'
1716

17+
import { getBlurHash, getDominantColor } from '~/utils/image'
18+
import { isVideoExt, pickImagesFromMarkdown } from '~/utils/markdown'
19+
1820
export const ImageDetailSection = defineComponent({
1921
props: {
2022
images: {
@@ -57,6 +59,7 @@ export const ImageDetailSection = defineComponent({
5759
width: existImageInfo?.width,
5860
type: existImageInfo?.type,
5961
accent: existImageInfo?.accent,
62+
blurHash: existImageInfo?.blurHash,
6063
} as any
6164
})
6265
.concat(props.images),
@@ -127,6 +130,7 @@ export const ImageDetailSection = defineComponent({
127130
src: item.src,
128131
type: ext,
129132
accent: getDominantColor($image),
133+
blurHash: getBlurHash($image),
130134
})
131135
})
132136
$image.onerror = (err) => {
@@ -232,6 +236,14 @@ export const ImageDetailSection = defineComponent({
232236
></NColorPicker>
233237
</NFormItem>
234238

239+
<NFormItem label="Blur Preview">
240+
<div>
241+
{image.blurHash && (
242+
<BlurHashPreview hash={image.blurHash} />
243+
)}
244+
</div>
245+
</NFormItem>
246+
235247
<NFormItem label="操作">
236248
<div class="flex w-full justify-end">
237249
<NButtonGroup>
@@ -267,3 +279,33 @@ export const ImageDetailSection = defineComponent({
267279
)
268280
},
269281
})
282+
283+
const BlurHashPreview = defineComponent({
284+
props: {
285+
hash: {
286+
type: String,
287+
required: true,
288+
},
289+
},
290+
setup(props) {
291+
const canvasRef = ref<HTMLCanvasElement | null>(null)
292+
293+
onMounted(() => {
294+
const canvas = canvasRef.value!
295+
const ctx = canvas.getContext('2d')!
296+
const pixels = decode(props.hash, 32, 32)
297+
const imageData = ctx.createImageData(32, 32)
298+
imageData.data.set(pixels)
299+
ctx.putImageData(imageData, 0, 0)
300+
})
301+
302+
return () => (
303+
<canvas
304+
ref={canvasRef}
305+
class="bg-cover bg-center"
306+
height={32}
307+
width={32}
308+
></canvas>
309+
)
310+
},
311+
})

src/models/base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export interface Image {
1212
type: string
1313
accent?: string
1414
src: string
15+
blurHash?: string
1516
}
1617

1718
export class BaseModel {

src/utils/image.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// @see https://stackoverflow.com/questions/2541481/get-average-color-of-image-via-javascript
2-
// @ts-nocheck
2+
3+
import { encode } from 'blurhash'
34

45
export function getDominantColor(imageObject: HTMLImageElement) {
56
const canvas = document.createElement('canvas'),
6-
ctx = canvas.getContext('2d')
7+
ctx = canvas.getContext('2d')!
78

89
canvas.width = 1
910
canvas.height = 1
@@ -26,3 +27,20 @@ export function rgbToHex(red: number, green: number, blue: number) {
2627
export function rgbObjectToHex(rgb: { r: number; g: number; b: number }) {
2728
return rgbToHex(rgb.r, rgb.g, rgb.b)
2829
}
30+
31+
export function getBlurHash(imageObject: HTMLImageElement) {
32+
const canvas = document.createElement('canvas'),
33+
ctx = canvas.getContext('2d')!
34+
35+
canvas.width = imageObject.naturalWidth
36+
canvas.height = imageObject.naturalHeight
37+
38+
ctx.drawImage(imageObject, 0, 0)
39+
40+
const imageData = ctx.getImageData(0, 0, 32, 32)
41+
const pixels = new Uint8ClampedArray(imageData.data)
42+
const componentX = 4
43+
const componentY = 4
44+
45+
return encode(pixels, 32, 32, componentX, componentY)
46+
}

0 commit comments

Comments
 (0)