1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge pull request #3144 from jellyfin/blurhash-worker

Decode blurhash in a WebWorker
This commit is contained in:
Bill Thornton 2022-01-14 09:15:44 -05:00 committed by GitHub
commit 0277726418
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 151 additions and 114 deletions

87
package-lock.json generated
View file

@ -3082,7 +3082,8 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz",
"integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==", "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==",
"dev": true "dev": true,
"requires": {}
}, },
"@webpack-cli/info": { "@webpack-cli/info": {
"version": "1.4.0", "version": "1.4.0",
@ -3097,7 +3098,8 @@
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz",
"integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==", "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==",
"dev": true "dev": true,
"requires": {}
}, },
"@xmldom/xmldom": { "@xmldom/xmldom": {
"version": "0.7.5", "version": "0.7.5",
@ -3142,13 +3144,15 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
"dev": true "dev": true,
"requires": {}
}, },
"acorn-jsx": { "acorn-jsx": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true "dev": true,
"requires": {}
}, },
"aggregate-error": { "aggregate-error": {
"version": "3.1.0", "version": "3.1.0",
@ -3205,7 +3209,8 @@
"version": "3.5.2", "version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true "dev": true,
"requires": {}
}, },
"alphanum-sort": { "alphanum-sort": {
"version": "1.0.2", "version": "1.0.2",
@ -4648,7 +4653,8 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.1.tgz", "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.1.tgz",
"integrity": "sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==", "integrity": "sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==",
"dev": true "dev": true,
"requires": {}
}, },
"csso": { "csso": {
"version": "4.2.0", "version": "4.2.0",
@ -5801,7 +5807,8 @@
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz",
"integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==", "integrity": "sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==",
"dev": true "dev": true,
"requires": {}
}, },
"eslint-rule-composer": { "eslint-rule-composer": {
"version": "0.3.0", "version": "0.3.0",
@ -6025,7 +6032,8 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-3.1.0.tgz", "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-3.1.0.tgz",
"integrity": "sha512-2RExSo0yJiqP+xiUue13jQa2IHE8kLDzTI7b6kn+vUlBVvlzNSiLDzo4e5Pp5J039usvTUnxZ8sUOhv0Kg15NA==", "integrity": "sha512-2RExSo0yJiqP+xiUue13jQa2IHE8kLDzTI7b6kn+vUlBVvlzNSiLDzo4e5Pp5J039usvTUnxZ8sUOhv0Kg15NA==",
"dev": true "dev": true,
"requires": {}
}, },
"express": { "express": {
"version": "4.17.2", "version": "4.17.2",
@ -7030,7 +7038,8 @@
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
"integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
"dev": true "dev": true,
"requires": {}
}, },
"idb": { "idb": {
"version": "6.1.2", "version": "6.1.2",
@ -7763,7 +7772,7 @@
}, },
"libass-wasm": { "libass-wasm": {
"version": "git+https://github.com/jellyfin/JavascriptSubtitlesOctopus.git#f4625ac313b318bd5d2e0ae18679ff516370bae6", "version": "git+https://github.com/jellyfin/JavascriptSubtitlesOctopus.git#f4625ac313b318bd5d2e0ae18679ff516370bae6",
"from": "git+https://github.com/jellyfin/JavascriptSubtitlesOctopus.git#4.0.0-jf-4" "from": "libass-wasm@git+https://github.com/jellyfin/JavascriptSubtitlesOctopus.git#4.0.0-jf-4"
}, },
"lie": { "lie": {
"version": "3.1.1", "version": "3.1.1",
@ -9058,25 +9067,29 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz",
"integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==", "integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==",
"dev": true "dev": true,
"requires": {}
}, },
"postcss-discard-duplicates": { "postcss-discard-duplicates": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz",
"integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==", "integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==",
"dev": true "dev": true,
"requires": {}
}, },
"postcss-discard-empty": { "postcss-discard-empty": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz",
"integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==", "integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==",
"dev": true "dev": true,
"requires": {}
}, },
"postcss-discard-overridden": { "postcss-discard-overridden": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz",
"integrity": "sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==", "integrity": "sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==",
"dev": true "dev": true,
"requires": {}
}, },
"postcss-double-position-gradients": { "postcss-double-position-gradients": {
"version": "3.0.4", "version": "3.0.4",
@ -9692,7 +9705,8 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
"integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
"dev": true "dev": true,
"requires": {}
}, },
"postcss-modules-local-by-default": { "postcss-modules-local-by-default": {
"version": "4.0.0", "version": "4.0.0",
@ -9748,7 +9762,8 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz",
"integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==", "integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==",
"dev": true "dev": true,
"requires": {}
}, },
"postcss-normalize-display-values": { "postcss-normalize-display-values": {
"version": "5.0.1", "version": "5.0.1",
@ -10150,7 +10165,8 @@
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.2.tgz", "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.2.tgz",
"integrity": "sha512-xfdkU128CkKKKVAwkyt0M8OdnelJ3MRcIRAPPQkRpoPeuzWY3RIeg7piRCpZ79MK7Q16diLXMMAD9dN5mauPlQ==", "integrity": "sha512-xfdkU128CkKKKVAwkyt0M8OdnelJ3MRcIRAPPQkRpoPeuzWY3RIeg7piRCpZ79MK7Q16diLXMMAD9dN5mauPlQ==",
"dev": true "dev": true,
"requires": {}
}, },
"postcss-selector-not": { "postcss-selector-not": {
"version": "5.0.0", "version": "5.0.0",
@ -10225,7 +10241,8 @@
"version": "0.36.2", "version": "0.36.2",
"resolved": "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.36.2.tgz", "resolved": "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.36.2.tgz",
"integrity": "sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==", "integrity": "sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==",
"dev": true "dev": true,
"requires": {}
}, },
"postcss-unique-selectors": { "postcss-unique-selectors": {
"version": "5.0.2", "version": "5.0.2",
@ -11495,6 +11512,14 @@
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"dev": true "dev": true
}, },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"string-width": { "string-width": {
"version": "4.2.2", "version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
@ -11542,14 +11567,6 @@
"define-properties": "^1.1.3" "define-properties": "^1.1.3"
} }
}, },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"stringify-entities": { "stringify-entities": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz",
@ -11633,7 +11650,8 @@
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz",
"integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==",
"dev": true "dev": true,
"requires": {}
}, },
"style-search": { "style-search": {
"version": "0.1.0", "version": "0.1.0",
@ -11845,7 +11863,8 @@
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz",
"integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==",
"dev": true "dev": true,
"requires": {}
}, },
"postcss-selector-parser": { "postcss-selector-parser": {
"version": "6.0.8", "version": "6.0.8",
@ -13663,7 +13682,8 @@
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-7.0.1.tgz", "resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-7.0.1.tgz",
"integrity": "sha512-iLBFYz6VRYyLJEJsBJ8M3TCqNcckVzz4wFounSc5Oez35ogE/X+aoC5fFu103Ot7NyvjU3/xqIXn93Gp3kJk4g==", "integrity": "sha512-iLBFYz6VRYyLJEJsBJ8M3TCqNcckVzz4wFounSc5Oez35ogE/X+aoC5fFu103Ot7NyvjU3/xqIXn93Gp3kJk4g==",
"dev": true "dev": true,
"requires": {}
} }
} }
}, },
@ -15340,15 +15360,6 @@
} }
} }
}, },
"worker-plugin": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-5.0.1.tgz",
"integrity": "sha512-Pn7+19jIiANcGuTSGdy+vrzyF+SGH03A5wV8iu4jRTMAOfAC9bNeiHo4+l5tPS7F0uvICMBv+h8UCvL7lunxcA==",
"dev": true,
"requires": {
"loader-utils": "^1.1.0"
}
},
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",

View file

@ -55,8 +55,7 @@
"webpack-cli": "^4.9.1", "webpack-cli": "^4.9.1",
"webpack-dev-server": "^4.7.2", "webpack-dev-server": "^4.7.2",
"webpack-merge": "^5.8.0", "webpack-merge": "^5.8.0",
"workbox-webpack-plugin": "^6.2.4", "workbox-webpack-plugin": "^6.2.4"
"worker-plugin": "^5.0.1"
}, },
"dependencies": { "dependencies": {
"@fontsource/noto-sans": "^4.5.1", "@fontsource/noto-sans": "^4.5.1",

View file

@ -0,0 +1,16 @@
/* eslint-disable no-restricted-globals */
import { decode } from 'blurhash';
self.onmessage = ({ data: { hash, width, height } }): void => {
try {
self.postMessage({
pixels: decode(hash, width, height),
hsh: hash,
width: width,
height: height
});
} catch {
throw new TypeError(`Blurhash ${hash} is not valid`);
}
};
/* eslint-enable no-restricted-globals */

View file

@ -1,7 +1,21 @@
import * as lazyLoader from '../lazyLoader/lazyLoaderIntersectionObserver'; import * as lazyLoader from '../lazyLoader/lazyLoaderIntersectionObserver';
import * as userSettings from '../../scripts/settings/userSettings'; import * as userSettings from '../../scripts/settings/userSettings';
import { decode, isBlurhashValid } from 'blurhash';
import './style.scss'; import './style.scss';
// eslint-disable-next-line compat/compat
const worker = new Worker(new URL('./blurhash.worker.ts', import.meta.url));
const targetDic = {};
worker.addEventListener(
'message',
({ data: { pixels, hsh, width, height } }) => {
const elems = targetDic[hsh];
if (elems && elems.length) {
for (const elem of elems) {
drawBlurhash(elem, pixels, width, height);
}
delete targetDic[hsh];
}
}
);
/* eslint-disable indent */ /* eslint-disable indent */
export function lazyImage(elem, source = elem.getAttribute('data-src')) { export function lazyImage(elem, source = elem.getAttribute('data-src')) {
@ -12,42 +26,45 @@ import './style.scss';
fillImageElement(elem, source); fillImageElement(elem, source);
} }
function itemBlurhashing(target, blurhashstr) { function drawBlurhash(target, pixels, width, height) {
if (isBlurhashValid(blurhashstr)) { const canvas = document.createElement('canvas');
// Although the default values recommended by Blurhash developers is 32x32, a size of 18x18 seems to be the sweet spot for us, canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
const imgData = ctx.createImageData(width, height);
imgData.data.set(pixels);
ctx.putImageData(imgData, 0, 0);
requestAnimationFrame(() => {
// This class is just an utility class, so users can customize the canvas using their own CSS.
canvas.classList.add('blurhash-canvas');
target.parentNode.insertBefore(canvas, target);
target.classList.add('blurhashed');
target.removeAttribute('data-blurhash');
});
}
function itemBlurhashing(target, hash) {
try {
// Although the default values recommended by Blurhash developers is 32x32, a size of 20x20 seems to be the sweet spot for us,
// improving the performance and reducing the memory usage, while retaining almost full blur quality. // improving the performance and reducing the memory usage, while retaining almost full blur quality.
// Lower values had more visible pixelation // Lower values had more visible pixelation
const width = 18; const width = 20;
const height = 18; const height = 20;
let pixels; targetDic[hash] = (targetDic[hash] || []).filter(item => item !== target);
try { targetDic[hash].push(target);
pixels = decode(blurhashstr, width, height);
} catch (err) {
console.error('Blurhash decode error: ', err);
target.classList.add('non-blurhashable');
return;
}
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
const imgData = ctx.createImageData(width, height);
imgData.data.set(pixels); worker.postMessage({
ctx.putImageData(imgData, 0, 0); hash,
width,
requestAnimationFrame(() => { height
canvas.classList.add('blurhash-canvas');
if (userSettings.enableFastFadein()) {
canvas.classList.add('lazy-blurhash-fadein-fast');
} else {
canvas.classList.add('lazy-blurhash-fadein');
}
target.parentNode.insertBefore(canvas, target);
target.classList.add('blurhashed');
target.removeAttribute('data-blurhash');
}); });
} catch (err) {
console.error(err);
target.classList.add('non-blurhashable');
return;
} }
} }
@ -65,14 +82,25 @@ import './style.scss';
} }
if (entry.intersectionRatio > 0) { if (entry.intersectionRatio > 0) {
if (source) fillImageElement(target, source); if (source) {
fillImageElement(target, source);
}
} else if (!source) { } else if (!source) {
requestAnimationFrame(() => { emptyImageElement(target);
emptyImageElement(target);
});
} }
} }
function onAnimationEnd(event) {
const elem = event.target;
requestAnimationFrame(() => {
const canvas = elem.previousSibling;
if (elem.classList.contains('blurhashed') && canvas && canvas.tagName === 'CANVAS') {
canvas.classList.add('lazy-hidden');
}
});
elem.removeEventListener('animationend', onAnimationEnd);
}
function fillImageElement(elem, url) { function fillImageElement(elem, url) {
if (url === undefined) { if (url === undefined) {
throw new TypeError('url cannot be undefined'); throw new TypeError('url cannot be undefined');
@ -82,6 +110,7 @@ import './style.scss';
preloaderImg.src = url; preloaderImg.src = url;
elem.classList.add('lazy-hidden'); elem.classList.add('lazy-hidden');
elem.addEventListener('animationend', onAnimationEnd);
preloaderImg.addEventListener('load', () => { preloaderImg.addEventListener('load', () => {
requestAnimationFrame(() => { requestAnimationFrame(() => {
@ -92,25 +121,22 @@ import './style.scss';
} }
elem.removeAttribute('data-src'); elem.removeAttribute('data-src');
elem.classList.remove('lazy-hidden');
if (userSettings.enableFastFadein()) { if (userSettings.enableFastFadein()) {
elem.classList.add('lazy-image-fadein-fast'); elem.classList.add('lazy-image-fadein-fast');
} else { } else {
elem.classList.add('lazy-image-fadein'); elem.classList.add('lazy-image-fadein');
} }
elem.classList.remove('lazy-hidden');
const canvas = elem.previousSibling;
if (elem.classList.contains('blurhashed') && canvas && canvas.tagName === 'CANVAS') {
canvas.classList.remove('lazy-image-fadein-fast', 'lazy-image-fadein');
canvas.classList.add('lazy-hidden');
}
}); });
}); });
} }
function emptyImageElement(elem) { function emptyImageElement(elem) {
// block repeated call - requestAnimationFrame twice for one image elem.removeEventListener('animationend', onAnimationEnd);
if (elem.getAttribute('data-src')) return; const canvas = elem.previousSibling;
if (canvas && canvas.tagName === 'CANVAS') {
canvas.classList.remove('lazy-hidden');
}
let url; let url;
@ -125,16 +151,6 @@ import './style.scss';
elem.classList.remove('lazy-image-fadein-fast', 'lazy-image-fadein'); elem.classList.remove('lazy-image-fadein-fast', 'lazy-image-fadein');
elem.classList.add('lazy-hidden'); elem.classList.add('lazy-hidden');
const canvas = elem.previousSibling;
if (canvas && canvas.tagName === 'CANVAS') {
canvas.classList.remove('lazy-hidden');
if (userSettings.enableFastFadein()) {
canvas.classList.add('lazy-image-fadein-fast');
} else {
canvas.classList.add('lazy-image-fadein');
}
}
} }
export function lazyChildren(elem) { export function lazyChildren(elem) {

View file

@ -1,17 +1,3 @@
.lazy-image-fadein {
opacity: 1;
transition: opacity 0.5s;
}
.lazy-image-fadein-fast {
opacity: 1;
transition: opacity 0.1s;
}
.lazy-hidden {
opacity: 0;
}
@keyframes fadein { @keyframes fadein {
from { from {
opacity: 0; opacity: 0;
@ -22,12 +8,18 @@
} }
} }
.lazy-blurhash-fadein-fast { .lazy-image-fadein {
opacity: 1;
animation: fadein 0.5s;
}
.lazy-image-fadein-fast {
opacity: 1;
animation: fadein 0.1s; animation: fadein 0.1s;
} }
.lazy-blurhash-fadein { .lazy-hidden {
animation: fadein 0.4s; opacity: 0;
} }
.blurhash-canvas { .blurhash-canvas {

View file

@ -13,7 +13,10 @@
callback(entry); callback(entry);
}); });
}, },
{rootMargin: '25%'}); {
rootMargin: '50%',
threshold: 0
});
this.observer = observer; this.observer = observer;
} }