PageSourceSearch

https://emptycan-2025.netlify.app/src/js/letter-shredder.js

js emptycan-2025.netlify.app collected 2026-10-03 10:41:55 UTC 8,786 bytes, 261 lines download raw bytes

1import lettersMapping from "./letters.js";
2
3let WORD = "EMPTYCAN";
4// let _RANDO = Math.floor(Math.random()*16777215).toString(16);
5let _BORDER = "#5581af";
6let _START = "#ffffff";
7let _FINAL = "yellow"; 
8let _S = (Math.random() * (window.innerWidth / 50)) + 15;
9let _P, _D;
10
11const _OFFSETY = 112;
12
13// ######################################################
14// thank you, ChatGPT, for the code and the laughs
15document.addEventListener("DOMContentLoaded", function() {
16    let canvas = document.getElementById('helloCanvas');
17    let ctx = canvas.getContext('2d');
18
19    let squareSize = window.innerWidth / 60; // size of each square
20    let spacing = 1; // space between letters
21
22    class RoundRect {
23        constructor(rectX, rectY, rectHeight, rectWidth, radius, color, c, e) {
24            this.rectX = rectX;
25            this.rectY = rectY;
26            this.rectWidth = rectWidth;
27            this.rectHeight = rectHeight;
28            this.radius = radius;
29            this.context = c;
30            this.color = color;
31            this.targetX = rectX;
32            this.targetY = rectY;
33    
34            const rect = canvas.getBoundingClientRect();
35            this.currentX = e.left + (Math.random() * e.width) - rect.left - squareSize;
36            this.currentY = e.top - rect.top - (squareSize/2);
37            // this.currentX = Math.random() * this.context.canvas.width;
38            // this.currentY = Math.random() * this.context.canvas.height;
39        }
40
41        draw() {
42            const c = this.context;
43            c.beginPath();
44            c.roundRect(this.currentX + this.rectWidth / 2, this.currentY,this.rectWidth, this.rectHeight, this.radius);
45            c.fillStyle = this.color;
46            c.fill();
47            c.lineWidth = 0.5;
48            c.stroke();
49        }
50
51        update() {
52            // Move the rectangle towards the target position
53            this.currentX += (this.targetX - this.currentX) * (Math.random() * 0.1);
54            this.currentY += (this.targetY - this.currentY) * (Math.random() * 0.1);
55
56            // Redraw the rectangle
57            //this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
58            this.draw();
59
60            // Continue the animation as long as the rectangle is not in position
61            if (!this.isInPosition()) {
62                requestAnimationFrame(this.update.bind(this));
63            } else {
64                this.context.fillStyle = _FINAL;
65                this.context.fill();
66            }
67        }
68
69        isInPosition() {
70            // Check if the rectangle is close enough to its target position
71            const distanceX = this.targetX - this.currentX;
72            const distanceY = this.targetY - this.currentY;
73            const distance = Math.sqrt(distanceX ** 2 + distanceY ** 2);
74            return distance < 0.5; // Consider it in position if less than 0.5 pixels away
75        }
76
77        animateToPosition() {
78            requestAnimationFrame(this.update.bind(this));
79        }
80    }
81
82    // Function to draw a letter
83    function drawLetter(letter, offsetX, offsetY, color) {
84        if (lettersMapping[letter]) {
85            lettersMapping[letter].forEach(squareOffset => {
86                let _X = offsetX + squareOffset[0] * squareSize, 
87                    _Y = offsetY + squareOffset[1] * squareSize;
88                
89                const myRoundRect = new RoundRect(
90                                        _X, _Y, 
91                                        squareSize, 
92                                        squareSize, 
93                                        1, color, ctx, _P);
94
95                myRoundRect.animateToPosition();
96                
97            });
98        }
99    }
100
101
102    // Function to draw the entire word
103    function drawWord(word, offsetX, offsetY, color) {
104        let currentPositionX = offsetX;
105        // Convert the word to uppercase to match the mappings
106        word = word.toUpperCase();
107        
108        word.split('').forEach(letter => {
109            drawLetter(letter, currentPositionX, offsetY, color);
110           
111            // Advance the current position for the next letter.
112            currentPositionX += (4 * squareSize) + spacing; // the width of the widest letter + spacing
113        });
114        
115        
116    }
117    function setup() {
118        squareSize = _S; // size of each square
119        spacing = 1; // space between letters
120        canvas.width = window.innerWidth;
121        canvas.height = window.innerHeight; // Fixed height to accommodate one letter vertically
122        ctx.fillStyle = "#FCF8B6";
123        ctx.lineWidth = 4;
124        ctx.strokeStyle = _BORDER;
125    }
126    function init() {
127        squareSize = Math.floor(Math.random() * (window.innerWidth / (4 * WORD.length)));
128        if (squareSize < 20)
129            squareSize = 20;
130        if ((squareSize < 10) && (window.innerWidth < 800))
131            squareSize = 10;
132        if ((window.innerWidth < 500))
133            squareSize = 9 *  (9 / WORD.length);
134        if (_D) {
135            drawWord(WORD, (canvas.width - (WORD.length * 4 * squareSize * spacing)) / 2, ((canvas.height - _OFFSETY) - 5 * squareSize) / 2, _START);       
136        }
137    }
138    function getOffset(el) {
139      const rect = el.getBoundingClientRect();
140      return {
141        left: rect.left + window.scrollX,
142        top: rect.top + window.scrollY - _OFFSETY/2,
143        width: rect.width
144      };
145    }
146    
147    // ######################################################
148    // UI controls
149    
150    const _SWATCH = document.querySelector("#color");
151    _SWATCH.addEventListener('change', (e) => {
152        _FINAL = e.target.value;
153        document.querySelector("label span").style.background = _FINAL;
154    });
155    
156    const _SAVE = document.querySelector("#save");
157    _SAVE.addEventListener('click', (e) => {
158        var link = document.createElement('a');
159        link.setAttribute('download', WORD + '.png');
160        link.setAttribute('href', canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"));
161        link.click();
162    });
163
164    const _INPUT = document.querySelector("input");
165    _INPUT.addEventListener('keyup', (e) => {
166        e.preventDefault();
167        e.stopPropagation();
168        if (e.key == 'Enter') {
169            
170            WORD = _INPUT.value.toUpperCase();
171            _D = true;
172            _P = getOffset(_INPUT);
173            init();
174        }
175    })
176
177    const _CLEAR = document.querySelector("#clear");
178    _CLEAR.addEventListener('click', (e) => {
179        document.querySelector("body canvas").remove();
180        let _canvas = document.createElement("canvas");
181        _canvas.setAttribute('id','helloCanvas');
182        document.querySelector("body main").appendChild(_canvas);
183        canvas = _canvas;
184        ctx = canvas.getContext('2d');
185        e.stopPropagation();
186        handleCanvas();
187        setup();
188    })
189    function handleCanvas() {
190        canvas.addEventListener('mousedown', (e) => {
191            // if (e.target instanceof HTMLCanvasElement) {
192            //     _P = {
193            //         left: e.clientX,
194            //         top: e.clientY,
195            //         width: 50
196            //       };
197            //     init();
198            // }
199            _P = {
200                left: e.clientX,
201                top: e.clientY,
202                width: 50
203              };
204            init();
205        });
206        canvas.addEventListener('mouseup', (e) => {
207            _D = false;
208        });
209        canvas.addEventListener('mousemove', (e) => {
210            _D = true;
211        });
212        canvas.oncontextmenu = function () {
213            _D = false;
214        }
215        canvas.addEventListener('mouseleave', (e) => {_D = false;});
216        canvas.addEventListener('mouseout', (e) => {_D = false;});
217        // ######################################################
218        // mousedown === touchstart
219        // mousemove === touchmove
220        // mouseup === touchend
221        canvas.addEventListener('touchstart', (e) => {
222            let _touches = e.targetTouches[0];
223            if (e.target instanceof HTMLCanvasElement) {
224                _P = {
225                    left: _touches.clientX,
226                    top: _touches.clientY,
227                    width: 50
228                  };
229                init();
230            }
231        });
232        canvas.addEventListener('touchmove', (e) => {
233            _D = true;
234        });
235        canvas.addEventListener('touchend', (e) => {
236            _D = false;
237        });
238    }
239    
240    window.addEventListener('resize', (e) => {
241        //setup();
242    });
243   
244
245    // ######################################################
246    // START ME UP, MIC
247    handleCanvas();
248    setup();
249    _D = true;
250    _P = getOffset(canvas);
251    init();
252    
253 
254 });
255
256
257
258
259
260
261

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.