-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathglitch.js
More file actions
194 lines (154 loc) · 7.26 KB
/
glitch.js
File metadata and controls
194 lines (154 loc) · 7.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// TODO
// make better performing line wrap
// dont animate stuff off screen
// opengl shader instead?????????? css?????????????? i dont know lol
// if this is true then all glitch text will have its text split into words
// where each word will get its own canvas
// this is a easy way to allow for some kind of word level wrapping
// but its also much slower if there are many words
// otherwise you could just keep this false and just manually separate text into
// multiple glitch elements where you want there to be wrapping
const splitWords = false;
function mapRange(dstMin, dstMax, srcMin, srcMax, n) {
const dstRange = dstMax - dstMin;
const srcRange = srcMax - srcMin;
const rangeRatio = dstRange / srcRange;
const nNormalized = (n - srcMin) / srcRange;
const nMapped = nNormalized * dstRange + dstMin;
return nMapped;
}
function makePreset(craziness) {
const preset = new Object();
preset.colorDistortionDistance = mapRange(0, 2, 0, 2.5, craziness);
preset.yStepJitter = mapRange(4, 1, 2.5, 4, craziness);
preset.shiftCoefficient = mapRange(0, 2, 0, 2.5, craziness);
return preset;
}
const canvasSettings = {};
function stripAlpha(string) {
if (string.startsWith('rgba')) {
const s = string.split(',');
const r = s[0].split('(')[1];
const g = s[1];
const b = s[2];
return 'rgb(' + r + ',' + g + ',' + b + ')';
} else {
return string;
}
}
function animationFrame(timestep) {
// get all the glitch canvas elements
const elements = document.querySelectorAll("[class^='glitch-canvas']");
for (let canvas of elements) {
const craziness = parseInt(canvas.className.split('-')[2]);
// use this preset to draw with
const preset = makePreset(craziness);
// text to draw
const settings = canvasSettings[canvas.cid];
const text = settings.text;
// style
const style = window.getComputedStyle(canvas);
const fontSize = style.getPropertyValue('font-size');
const fontFamily = style.getPropertyValue('font-family');
// create a buffer for drawing the undistorted text
const buffer = document.createElement('canvas');
const bufferContext = buffer.getContext('2d');
// draw vanilla onto buffer
bufferContext.font = fontSize + ' ' + fontFamily;
const metrics = bufferContext.measureText(text);
canvas.width = metrics.width;
canvas.height = metrics.actualBoundingBoxAscent;
buffer.width = canvas.width * 1.3; // Account for the bottom of the canvas. Keeps the bottom portions of letters such as g or p.
buffer.height = canvas.height;
bufferContext.font = fontSize + ' ' + fontFamily;
// draw it with colors separated
const backgroundColor = stripAlpha(window.getComputedStyle(document.body).getPropertyValue('background-color'));
const foregroundColor = stripAlpha(style.getPropertyValue('color'));
bufferContext.fillStyle = backgroundColor;
bufferContext.fillRect(0, 0, canvas.width, canvas.height);
bufferContext.fillStyle = foregroundColor;
bufferContext.fillText(text, 0, canvas.height);
// prepare to draw distorted text on screen canvas
const context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
context.globalCompositeOperation = 'lighter';
// temporary buffer for holding scanlines
const scanlineBuffer = document.createElement('canvas');
scanlineBuffer.width = canvas.width;
const scanlineContext = scanlineBuffer.getContext('2d');
scanlineContext.globalCompositeOperation = 'multiply';
// draw the rgb distorted buffer onto the screen one scanline at a time with jagged scanlines
let yStep = 0;
for (y = 0; y < canvas.height; y += yStep) {
// let yStep jitter
yStep = Math.floor(Math.random() * preset.yStepJitter + 1);
if (yStep < 1)
yStep = 1;
// shift horizontally some amount
const x = (Math.random() * 2 - 1) * Math.PI / 2 * preset.shiftCoefficient;
// prepare to draw scanline
scanlineBuffer.height = yStep;
// clip to scanline
context.save();
context.rect(0, y, canvas.width, yStep);
context.clip();
// red
scanlineContext.globalCompositeOperation = 'source-over';
scanlineContext.fillStyle = backgroundColor;
scanlineContext.fillRect(0, 0, canvas.width, yStep);
scanlineContext.drawImage(buffer, x, -y);
scanlineContext.globalCompositeOperation = 'multiply';
scanlineContext.fillStyle = '#ff0000'; // red
scanlineContext.fillRect(0, 0, canvas.width, yStep);
context.drawImage(scanlineBuffer, 0, y);
// green
scanlineContext.globalCompositeOperation = 'source-over';
scanlineContext.fillStyle = backgroundColor;
scanlineContext.fillRect(0, 0, canvas.width, yStep);
scanlineContext.drawImage(buffer, x, -y - preset.colorDistortionDistance);
scanlineContext.globalCompositeOperation = 'multiply';
scanlineContext.fillStyle = '#00ff00'; // green
scanlineContext.fillRect(0, 0, canvas.width, yStep);
context.drawImage(scanlineBuffer, 0, y);
// blue
scanlineContext.globalCompositeOperation = 'source-over';
scanlineContext.fillStyle = backgroundColor;
scanlineContext.fillRect(0, 0, canvas.width, yStep);
scanlineContext.drawImage(buffer, x - preset.colorDistortionDistance, -y);
scanlineContext.globalCompositeOperation = 'multiply';
scanlineContext.fillStyle = '#0000ff'; // blue
scanlineContext.fillRect(0, 0, canvas.width, yStep);
context.drawImage(scanlineBuffer, 0, y);
context.restore();
}
}
// continue the animation forever
window.requestAnimationFrame(animationFrame);
}
window.addEventListener('load', event => {
// get all the elements with glitch class
const elements = document.querySelectorAll("[class^='glitch']");
for (let element of elements) {
const craziness = element.className.split('-')[1];
const text = element.innerText;
// split into words
const words = splitWords ? (text + ' ').match(/\b(\w+\W+)/g) : [text];
// create the element to hold all the canvases
const container = document.createElement('span');
for (let word of words) {
// we r going to replace the text with a canvas that we will draw on
const canvas = document.createElement('canvas');
canvas.cid = Math.random()
canvas.className = 'glitch-canvas-' + craziness;
const settings = new Object();
settings.text = word;
canvasSettings[canvas.cid] = settings;
// add child to container
container.appendChild(canvas);
}
// replace it
element.parentNode.replaceChild(container, element);
}
// now we want to animate all of the elements each frame of rendering the page
window.requestAnimationFrame(animationFrame);
});