├── .babelrc
├── .editorconfig
├── .eslintrc
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── .prettierrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── docs-assets
└── thumbnail.jpg
├── package-lock.json
├── package.json
├── screenshot.png
├── src
├── blur.jsx
└── lib
│ └── StackBlur.js
└── website
├── .editorconfig
├── .env
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── public
├── example-kyoto.jpg
├── index.html
├── manifest.json
├── react-blur.jpg
└── robots.txt
└── src
├── app.jsx
├── index.js
└── serviceWorker.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "react",
4 | "es2015",
5 | "stage-1"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 |
10 | [*.md]
11 | max_line_length = off
12 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": [
4 | "airbnb",
5 | "prettier",
6 | "prettier/react"
7 | ],
8 | "plugins": [
9 | "prettier"
10 | ],
11 | "env": {
12 | "browser": true
13 | },
14 | "rules":{
15 | "prettier/prettier": [1, { "trailingComma": "es5", "singleQuote": true }]
16 | }
17 | }
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "es2021": true
5 | },
6 | "extends": [
7 | "eslint:recommended",
8 | "plugin:react/recommended"
9 | ],
10 | "parserOptions": {
11 | "ecmaFeatures": {
12 | "jsx": true
13 | },
14 | "ecmaVersion": 12,
15 | "sourceType": "module"
16 | },
17 | "plugins": [
18 | "react"
19 | ],
20 | "rules": {
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /dist
5 | .vercel
6 | /node_modules
7 | /.pnp
8 | .pnp.js
9 |
10 | # testing
11 | /coverage
12 |
13 | # production
14 | /build
15 |
16 | # misc
17 | .DS_Store
18 | .env.local
19 | .env.development.local
20 | .env.test.local
21 | .env.production.local
22 |
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | **/node_modules
2 | *.log
3 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "printWidth": 80,
4 | "editor.formatOnSave": true,
5 | "proseWrap": "always",
6 | "tabWidth": 2,
7 | "requireConfig": false,
8 | "useTabs": false,
9 | "trailingComma": "none",
10 | "bracketSpacing": true,
11 | "jsxBracketSameLine": false,
12 | "semi": true
13 | }
14 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 1.0.3
2 | - Remove `console.log`.
3 |
4 | # 1.0.2
5 | - Dependency updates.
6 |
7 | # 1.0.1
8 | - Fix resizing
9 |
10 | # 1.0.0
11 | - Add `requestAnimationFrame` to prevent extra work.
12 | - Update dependencies.
13 |
14 | # 0.6.0
15 | - Add conditional flag for resizing by @TheHaff
16 |
17 | # 0.5.2
18 | - Fix issue when the child canvas didn't fill the parent container. Thank @getmicah !
19 |
20 | # 0.5.1
21 | - Prevents recursive calls caused by setting this.img.src to a falsey value. eg. an empty string. (thanks @joshgillies!).
22 |
23 | # 0.5.0
24 | - Fix gitignore for MAC
25 | - Fix undeclared vars
26 | - add onLoadFunction prop for hook into the blur loading.
27 | - make sure changing src keeps correct blurRadius
28 | - Do not reload image if the img src prop is relative (because it won't match this.img.src which is absolute)
29 |
30 | Thanks @iamJoeTaylor!
31 |
32 | # 0.3.1
33 | - Fix `react-addons-pure-render-mixin` dependency bug. (thanks @bogas04!).
34 |
35 | # 0.3.0
36 | - Added support for react 0.14.x (thanks @voronianski!).
37 |
38 | # 0.2.5
39 | - Update image if a new image src is passed in props [ab54d60d]
40 |
41 | # 0.2.4
42 | - Prevent the component to crash if there was a CORS error.
43 |
44 | # 0.2.3
45 | - Stop wasting renders on componentWillReceiveProps, using componentWillUpdate instead.
46 |
47 | # 0.2.0
48 | - Assets are now precompiled.
49 | - Changed `resizeSpeed` for `resizeInterval`.
50 |
51 | # v0.1.0
52 | - Added `resizeSpeed` prop.
53 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2019, Javier Bórquez
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [React Blur](https://javier.xyz/react-blur/)
2 |
3 | React component for creating blurred backgrounds using canvas.
4 |
5 | [](https://javier.xyz/react-blur/)
6 |
7 | ## Installation
8 |
9 | ```js
10 | npm install react-blur --save
11 | ```
12 |
13 | ## Usage
14 |
15 | ```js
16 | import Blur from "react-blur";
17 | ```
18 |
19 | ### Example
20 |
21 | ```jsx
22 |
23 | The content.
24 |
25 | ```
26 |
27 | For a complete example see the code in the [demo branch](https://github.com/javierbyte/react-blur/blob/gh-pages/src/js/app.jsx).
28 |
29 | #### Props
30 |
31 | - `img`: The image path.
32 | - `blurRadius`: Optional. The size of the blur radius.
33 | - `enableStyles`: Optional. Flag to include base styles inline, omit this to easily override.
34 | - `shouldResize`: Optional. If the canvas should re-render on resize? Defaults to true.
35 | - `resizeInterval`: Optional. How fast the canvas should re-render on resize? Defaults to 128ms.
36 |
37 | ### Contributing
38 |
39 | _Thanks to [Quasimodo](http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html) for the original stack blur algorithm._
40 |
--------------------------------------------------------------------------------
/docs-assets/thumbnail.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/javierbyte/react-blur/11395ceb644ab3b7072077ccd4f72053912f5c8f/docs-assets/thumbnail.jpg
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-blur",
3 | "version": "1.0.3",
4 | "description": "React component for creating blurred backgrounds.",
5 | "main": "dist/blur.js",
6 | "scripts": {
7 | "build": "mkdir -p dist/lib && babel src/blur.jsx -u -o dist/blur.js && babel src/lib/StackBlur.js -u -o dist/lib/StackBlur.js",
8 | "prepublish": "mkdir -p dist && babel src/blur.jsx -u -o dist/blur.js && babel src/lib/StackBlur.js -u -o dist/lib/StackBlur.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [
12 | "blur",
13 | "canvas",
14 | "component",
15 | "react",
16 | "react-component",
17 | "select",
18 | "text"
19 | ],
20 | "repository": {
21 | "type": "git",
22 | "url": "http://github.com/javierbyte/react-blur.git"
23 | },
24 | "author": "hi@javier.xyz",
25 | "license": "BSD-3-Clause",
26 | "peerDependencies": {
27 | "react": "^16.8.2"
28 | },
29 | "dependencies": {
30 | "prop-types": "^15.8.0"
31 | },
32 | "devDependencies": {
33 | "babel-cli": "^6.9.0",
34 | "babel-eslint": "^10.1.0",
35 | "babel-preset-es2015": "^6.9.0",
36 | "babel-preset-react": "^6.5.0",
37 | "babel-preset-stage-1": "^6.5.0",
38 | "eslint": "^8.6.0",
39 | "eslint-config-airbnb": "^19.0.4",
40 | "eslint-config-prettier": "^8.3.0",
41 | "eslint-plugin-babel": "^5.3.1",
42 | "eslint-plugin-import": "^2.25.4",
43 | "eslint-plugin-jsx-a11y": "^6.5.1",
44 | "eslint-plugin-prettier": "^4.0.0",
45 | "eslint-plugin-react": "^7.28.0",
46 | "prettier": "^2.5.1"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/javierbyte/react-blur/11395ceb644ab3b7072077ccd4f72053912f5c8f/screenshot.png
--------------------------------------------------------------------------------
/src/blur.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from "prop-types";
2 | import React, { useEffect, useRef } from "react";
3 | import stackBlurImage from "./lib/StackBlur.js";
4 |
5 | let animating = false;
6 |
7 | const ReactBlur = (props) => {
8 | const {
9 | blurRadius = 0,
10 | children = null,
11 | className = "",
12 | enableStyles = false,
13 | onLoadFunction = () => {},
14 | shouldResize = false,
15 | resizeInterval = 64,
16 | img,
17 | ...other
18 | } = props;
19 |
20 | const containerRef = useRef();
21 | const canvasRef = useRef();
22 | const imgRef = useRef();
23 | const widthRef = useRef();
24 | const heightRef = useRef();
25 |
26 | useEffect(() => {
27 | loadImage();
28 |
29 | if (shouldResize) {
30 | window.addEventListener("resize", resize);
31 | }
32 |
33 | return () => {
34 | window.removeEventListener("resize", resize);
35 | };
36 | }, []);
37 |
38 | useEffect(() => {
39 | if (!imgRef.current) {
40 | loadImage(props);
41 | } else if (!isCurrentImgSrc(img)) {
42 | imgRef.current.src = img;
43 | setDimensions();
44 | } else {
45 | // if some other prop changed reblur
46 | if (animating) {
47 | return;
48 | }
49 |
50 | animating = true;
51 |
52 | window.requestAnimationFrame(() => {
53 | stackBlurImage(
54 | imgRef.current,
55 | canvasRef.current,
56 | blurRadius,
57 | widthRef.current,
58 | heightRef.current,
59 | () => {
60 | animating = false;
61 | }
62 | );
63 | });
64 | }
65 | });
66 |
67 | const setDimensions = () => {
68 | heightRef.current = containerRef.current.offsetHeight;
69 | widthRef.current = containerRef.current.offsetWidth;
70 | canvasRef.current.height = heightRef.current;
71 | canvasRef.current.width = widthRef.current;
72 | };
73 |
74 | const isCurrentImgSrc = (newSrc) => {
75 | // Handle relative paths
76 | if (imgRef.current) {
77 | const newImg = new Image();
78 | newImg.src = newSrc;
79 | // if absolute SRC is the same
80 | return newImg.src === imgRef.current.src;
81 | }
82 |
83 | return false;
84 | };
85 |
86 | const loadImage = () => {
87 | if (isCurrentImgSrc(imgRef.current)) {
88 | stackBlurImage(
89 | imgRef.current,
90 | canvasRef.current,
91 | blurRadius,
92 | widthRef.current,
93 | heightRef.current
94 | );
95 | return;
96 | }
97 |
98 | imgRef.current = new Image();
99 | imgRef.current.crossOrigin = "Anonymous";
100 |
101 | imgRef.current.onload = (event) => {
102 | stackBlurImage(
103 | imgRef.current,
104 | canvasRef.current,
105 | blurRadius,
106 | widthRef.current,
107 | heightRef.current
108 | );
109 | onLoadFunction(event);
110 | };
111 |
112 | imgRef.current.onerror = (event) => {
113 | // Remove the onerror listener.
114 | // Preventing recursive calls caused by setting this.img.src to a falsey value
115 | imgRef.current.onerror = undefined;
116 |
117 | imgRef.current.src = "";
118 | onLoadFunction(event);
119 | };
120 | imgRef.current.src = img;
121 |
122 | setDimensions();
123 | };
124 |
125 | let last;
126 | const resize = () => {
127 | const now = new Date().getTime();
128 | let deferTimer;
129 | const threshhold = resizeInterval;
130 |
131 | if (last && now < last + threshhold) {
132 | clearTimeout(deferTimer);
133 | deferTimer = setTimeout(() => {
134 | last = now;
135 | doResize();
136 | }, threshhold);
137 | } else {
138 | last = now;
139 | doResize();
140 | }
141 | };
142 |
143 | const doResize = () => {
144 | setDimensions();
145 |
146 | stackBlurImage(
147 | imgRef.current,
148 | canvasRef.current,
149 | blurRadius,
150 | widthRef.current,
151 | heightRef.current
152 | );
153 | };
154 |
155 | let classes = "react-blur";
156 | if (className) {
157 | classes += ` ${className}`;
158 | }
159 |
160 | const containerStyle = enableStyles
161 | ? {
162 | position: "relative",
163 | }
164 | : {};
165 | const canvasStyle = enableStyles
166 | ? {
167 | position: "absolute",
168 | top: 0,
169 | left: 0,
170 | }
171 | : {};
172 |
173 | return (
174 |
180 |
185 | {children}
186 |
187 | );
188 | };
189 |
190 | ReactBlur.propTypes = {
191 | blurRadius: PropTypes.number,
192 | children: PropTypes.node,
193 | className: PropTypes.string,
194 | enableStyles: PropTypes.bool,
195 | img: PropTypes.string.isRequired,
196 | onLoadFunction: PropTypes.func,
197 | shouldResize: PropTypes.bool,
198 | resizeInterval: PropTypes.number,
199 | };
200 |
201 | export default ReactBlur;
202 |
--------------------------------------------------------------------------------
/src/lib/StackBlur.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | var mul_table = [
4 | 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454, 405, 364, 328, 298, 271, 496,
5 | 456, 420, 388, 360, 335, 312, 292, 273, 512, 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496,
6 | 475, 456, 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, 497, 482, 468, 454, 441,
7 | 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496,
8 | 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318,
9 | 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454, 447, 441,
10 | 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328, 324,
11 | 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271, 268, 265, 262, 259, 257, 507, 501, 496,
12 | 491, 485, 480, 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392,
13 | 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, 332, 329, 326, 323, 320, 318,
14 | 315, 312, 310, 307, 304, 302, 299, 297, 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263,
15 | 261, 259,
16 | ];
17 |
18 | var shg_table = [
19 | 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18,
20 | 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
21 | 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
22 | 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
23 | 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
24 | 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
25 | 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
26 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
27 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
28 | ];
29 |
30 | function stackBlurImage(img, canvas, radius, w, h, callback) {
31 | if (!w || !h) return;
32 | var nw = img.naturalWidth;
33 | var nh = img.naturalHeight;
34 |
35 | canvas.style.width = w + "px";
36 | canvas.style.height = h + "px";
37 | canvas.width = w;
38 | canvas.height = h;
39 |
40 | var context = canvas.getContext("2d");
41 | context.clearRect(0, 0, w, h);
42 |
43 | var ratio = 1;
44 | if (nw / w > nh / h) {
45 | ratio = nh / h;
46 | } else {
47 | ratio = nw / w;
48 | }
49 |
50 | var drawW = nw / ratio;
51 | var drawH = nh / ratio;
52 |
53 | try {
54 | context.drawImage(
55 | img,
56 | Math.floor((drawW - w) / -2),
57 | Math.floor((drawH - h) / -2),
58 | Math.ceil(drawW),
59 | Math.ceil(drawH)
60 | );
61 | } catch (e) {
62 | console.error("There was a problem drawing the image. " + e);
63 | }
64 |
65 | if (isNaN(radius) || radius < 1) {
66 | callback(null);
67 | return;
68 | }
69 | stackBlurCanvasRGB(canvas, 0, 0, w, h, radius, callback);
70 | }
71 |
72 | function stackBlurCanvasRGB(canvas, top_x, top_y, width, height, radius, callback) {
73 | if (isNaN(radius) || radius < 1) return;
74 | radius |= 0;
75 |
76 | var context = canvas.getContext("2d");
77 | var imageData;
78 |
79 | try {
80 | imageData = context.getImageData(top_x, top_y, width, height);
81 | } catch (e) {
82 | throw new Error("unable to access image data: " + e);
83 | }
84 |
85 | var pixels = imageData.data;
86 |
87 | var x,
88 | y,
89 | i,
90 | p,
91 | yp,
92 | yi,
93 | yw,
94 | r_sum,
95 | g_sum,
96 | b_sum,
97 | r_out_sum,
98 | g_out_sum,
99 | b_out_sum,
100 | r_in_sum,
101 | g_in_sum,
102 | b_in_sum,
103 | pr,
104 | pg,
105 | pb,
106 | rbs;
107 |
108 | var div = radius + radius + 1;
109 | var w4 = width << 2;
110 | var widthMinus1 = width - 1;
111 | var heightMinus1 = height - 1;
112 | var radiusPlus1 = radius + 1;
113 | var sumFactor = (radiusPlus1 * (radiusPlus1 + 1)) / 2;
114 |
115 | var stackStart = new BlurStack();
116 | var stack = stackStart;
117 | for (i = 1; i < div; i++) {
118 | stack = stack.next = new BlurStack();
119 | if (i == radiusPlus1) var stackEnd = stack;
120 | }
121 | stack.next = stackStart;
122 | var stackIn = null;
123 | var stackOut = null;
124 |
125 | yw = yi = 0;
126 |
127 | var mul_sum = mul_table[radius];
128 | var shg_sum = shg_table[radius];
129 |
130 | for (y = 0; y < height; y++) {
131 | r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0;
132 |
133 | r_out_sum = radiusPlus1 * (pr = pixels[yi]);
134 | g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
135 | b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);
136 |
137 | r_sum += sumFactor * pr;
138 | g_sum += sumFactor * pg;
139 | b_sum += sumFactor * pb;
140 |
141 | stack = stackStart;
142 |
143 | for (i = 0; i < radiusPlus1; i++) {
144 | stack.r = pr;
145 | stack.g = pg;
146 | stack.b = pb;
147 | stack = stack.next;
148 | }
149 |
150 | for (i = 1; i < radiusPlus1; i++) {
151 | p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2);
152 | r_sum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - i);
153 | g_sum += (stack.g = pg = pixels[p + 1]) * rbs;
154 | b_sum += (stack.b = pb = pixels[p + 2]) * rbs;
155 |
156 | r_in_sum += pr;
157 | g_in_sum += pg;
158 | b_in_sum += pb;
159 |
160 | stack = stack.next;
161 | }
162 |
163 | stackIn = stackStart;
164 | stackOut = stackEnd;
165 | for (x = 0; x < width; x++) {
166 | pixels[yi] = (r_sum * mul_sum) >> shg_sum;
167 | pixels[yi + 1] = (g_sum * mul_sum) >> shg_sum;
168 | pixels[yi + 2] = (b_sum * mul_sum) >> shg_sum;
169 |
170 | r_sum -= r_out_sum;
171 | g_sum -= g_out_sum;
172 | b_sum -= b_out_sum;
173 |
174 | r_out_sum -= stackIn.r;
175 | g_out_sum -= stackIn.g;
176 | b_out_sum -= stackIn.b;
177 |
178 | p = (yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1)) << 2;
179 |
180 | r_in_sum += stackIn.r = pixels[p];
181 | g_in_sum += stackIn.g = pixels[p + 1];
182 | b_in_sum += stackIn.b = pixels[p + 2];
183 |
184 | r_sum += r_in_sum;
185 | g_sum += g_in_sum;
186 | b_sum += b_in_sum;
187 |
188 | stackIn = stackIn.next;
189 |
190 | r_out_sum += pr = stackOut.r;
191 | g_out_sum += pg = stackOut.g;
192 | b_out_sum += pb = stackOut.b;
193 |
194 | r_in_sum -= pr;
195 | g_in_sum -= pg;
196 | b_in_sum -= pb;
197 |
198 | stackOut = stackOut.next;
199 |
200 | yi += 4;
201 | }
202 | yw += width;
203 | }
204 |
205 | for (x = 0; x < width; x++) {
206 | g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0;
207 |
208 | yi = x << 2;
209 | r_out_sum = radiusPlus1 * (pr = pixels[yi]);
210 | g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
211 | b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);
212 |
213 | r_sum += sumFactor * pr;
214 | g_sum += sumFactor * pg;
215 | b_sum += sumFactor * pb;
216 |
217 | stack = stackStart;
218 |
219 | for (i = 0; i < radiusPlus1; i++) {
220 | stack.r = pr;
221 | stack.g = pg;
222 | stack.b = pb;
223 | stack = stack.next;
224 | }
225 |
226 | yp = width;
227 |
228 | for (i = 1; i <= radius; i++) {
229 | yi = (yp + x) << 2;
230 |
231 | r_sum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i);
232 | g_sum += (stack.g = pg = pixels[yi + 1]) * rbs;
233 | b_sum += (stack.b = pb = pixels[yi + 2]) * rbs;
234 |
235 | r_in_sum += pr;
236 | g_in_sum += pg;
237 | b_in_sum += pb;
238 |
239 | stack = stack.next;
240 |
241 | if (i < heightMinus1) {
242 | yp += width;
243 | }
244 | }
245 |
246 | yi = x;
247 | stackIn = stackStart;
248 | stackOut = stackEnd;
249 | for (y = 0; y < height; y++) {
250 | p = yi << 2;
251 | pixels[p] = (r_sum * mul_sum) >> shg_sum;
252 | pixels[p + 1] = (g_sum * mul_sum) >> shg_sum;
253 | pixels[p + 2] = (b_sum * mul_sum) >> shg_sum;
254 |
255 | r_sum -= r_out_sum;
256 | g_sum -= g_out_sum;
257 | b_sum -= b_out_sum;
258 |
259 | r_out_sum -= stackIn.r;
260 | g_out_sum -= stackIn.g;
261 | b_out_sum -= stackIn.b;
262 |
263 | p = (x + ((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width) << 2;
264 |
265 | r_sum += r_in_sum += stackIn.r = pixels[p];
266 | g_sum += g_in_sum += stackIn.g = pixels[p + 1];
267 | b_sum += b_in_sum += stackIn.b = pixels[p + 2];
268 |
269 | stackIn = stackIn.next;
270 |
271 | r_out_sum += pr = stackOut.r;
272 | g_out_sum += pg = stackOut.g;
273 | b_out_sum += pb = stackOut.b;
274 |
275 | r_in_sum -= pr;
276 | g_in_sum -= pg;
277 | b_in_sum -= pb;
278 |
279 | stackOut = stackOut.next;
280 |
281 | yi += width;
282 | }
283 | }
284 | context.putImageData(imageData, top_x, top_y);
285 | if (callback) {
286 | callback(null);
287 | }
288 | }
289 |
290 | function BlurStack() {
291 | this.r = 0;
292 | this.g = 0;
293 | this.b = 0;
294 | this.a = 0;
295 | this.next = null;
296 | }
297 |
298 | module.exports = stackBlurImage;
299 |
--------------------------------------------------------------------------------
/website/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 |
10 | [*.md]
11 | max_line_length = off
12 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/website/.env:
--------------------------------------------------------------------------------
1 | SKIP_PREFLIGHT_CHECK=true
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /dist
5 | .vercel
6 | /node_modules
7 | /.pnp
8 | .pnp.js
9 |
10 | # testing
11 | /coverage
12 |
13 | # production
14 | /build
15 |
16 | # misc
17 | .DS_Store
18 | .env.local
19 | .env.development.local
20 | .env.test.local
21 | .env.production.local
22 |
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
--------------------------------------------------------------------------------
/website/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "printWidth": 80,
4 | "editor.formatOnSave": true,
5 | "proseWrap": "always",
6 | "tabWidth": 2,
7 | "requireConfig": false,
8 | "useTabs": false,
9 | "trailingComma": "none",
10 | "bracketSpacing": true,
11 | "jsxBracketSameLine": false,
12 | "semi": true
13 | }
14 |
--------------------------------------------------------------------------------
/website/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2021, Javier Bórquez
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/website/README.md:
--------------------------------------------------------------------------------
1 | # react-blur-website
2 |
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-blur-website",
3 | "description": "React component for creating blurred backgrounds using canvas.",
4 | "version": "1.0.0",
5 | "private": true,
6 | "homepage": "https://javier.xyz/react-blur/",
7 | "dependencies": {
8 | "capsize": "^2.0.0",
9 | "jbx": "^1.7.5",
10 | "react": "^17.0.2",
11 | "react-blur": "^1.0.3",
12 | "react-dom": "^17.0.2",
13 | "react-scripts": "^5.0.0",
14 | "styled-components": "^5.3.3"
15 | },
16 | "scripts": {
17 | "dev": "react-scripts start",
18 | "start": "react-scripts start",
19 | "build": "react-scripts build",
20 | "eject": "react-scripts eject",
21 | "predeploy": "npm run build",
22 | "deploy": "gh-pages -d build"
23 | },
24 | "eslintConfig": {
25 | "extends": "react-app"
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.5%",
30 | "not dead",
31 | "not IE 11",
32 | "not IE_Mob 11",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | },
41 | "repository": {
42 | "type": "git",
43 | "url": "https://github.com/javierbyte/react-blur-website"
44 | },
45 | "author": "Javier Bórquez ",
46 | "license": "BSD-3-Clause",
47 | "devDependencies": {
48 | "gh-pages": "^3.1.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/website/public/example-kyoto.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/javierbyte/react-blur/11395ceb644ab3b7072077ccd4f72053912f5c8f/website/public/example-kyoto.jpg
--------------------------------------------------------------------------------
/website/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | React Blur | React component for creating blurred backgrounds using canvas.
9 |
13 |
17 |
18 |
19 |
20 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/website/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React Blur",
3 | "name": "React component for creating blurred backgrounds using canvas.",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "theme_color": "#000000",
7 | "background_color": "#ffffff"
8 | }
9 |
--------------------------------------------------------------------------------
/website/public/react-blur.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/javierbyte/react-blur/11395ceb644ab3b7072077ccd4f72053912f5c8f/website/public/react-blur.jpg
--------------------------------------------------------------------------------
/website/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/website/src/app.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 |
3 | import {
4 | JBX,
5 | MainHeader,
6 | Text,
7 | Space,
8 | Container,
9 | Range,
10 | Box,
11 | A,
12 | CodeSnippet
13 | } from 'jbx';
14 |
15 | import Blur from 'react-blur';
16 |
17 | const exampleCode = `
18 | // npm install react-blur
19 | import Blur from 'react-blur'
20 | [...]
21 |
22 | `.trim();
23 |
24 | function App() {
25 | const [blurSrc, blurSet] = useState(16);
26 |
27 | const blur = Math.max(blurSrc, 0);
28 |
29 | return (
30 |
31 |
32 | react-blur
33 |
34 |
35 | React component for creating blurred backgrounds using canvas.
36 |
37 |
38 |
39 |
48 |
49 |
50 | Amount of blur {blur}px
51 |
52 |
53 | blurSet(Number(e.target.value))}
58 | step={1}
59 | min={0}
60 | max={64}
61 | />
62 |
63 |
64 | How to use:
65 |
66 | {exampleCode}
67 |
68 |
69 | For more information see the{' '}
70 | github repo.
71 |
72 |
73 |
74 | Made by javierbyte.
75 |
76 |
77 | );
78 | }
79 |
80 | export default App;
81 |
--------------------------------------------------------------------------------
/website/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './app.jsx';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/website/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------