hashvatar 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +131 -34
- package/dist/index.js +131 -34
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -155,7 +155,85 @@ var SHAPES = [
|
|
|
155
155
|
[[0.15, 0.2], [0.55, 0.08], [0.92, 0.35], [0.78, 0.75], [0.4, 0.92], [0.2, 0.6]],
|
|
156
156
|
[[0.45, 0.08], [0.82, 0.3], [0.7, 0.85], [0.3, 0.88], [0.08, 0.5], [0.25, 0.25]]
|
|
157
157
|
];
|
|
158
|
-
|
|
158
|
+
var _filterSupported = null;
|
|
159
|
+
function hasFilterBlur() {
|
|
160
|
+
if (_filterSupported !== null) return _filterSupported;
|
|
161
|
+
try {
|
|
162
|
+
const c = document.createElement("canvas");
|
|
163
|
+
c.width = 30;
|
|
164
|
+
c.height = 30;
|
|
165
|
+
const x = c.getContext("2d");
|
|
166
|
+
x.fillStyle = "#fff";
|
|
167
|
+
x.fillRect(0, 0, 30, 30);
|
|
168
|
+
x.filter = "blur(6px)";
|
|
169
|
+
x.fillStyle = "#000";
|
|
170
|
+
x.fillRect(12, 12, 4, 4);
|
|
171
|
+
_filterSupported = x.getImageData(22, 22, 1, 1).data[0] < 255;
|
|
172
|
+
x.filter = "none";
|
|
173
|
+
} catch {
|
|
174
|
+
_filterSupported = false;
|
|
175
|
+
}
|
|
176
|
+
return _filterSupported;
|
|
177
|
+
}
|
|
178
|
+
function boxBlurCanvas(ctx, w, h, radius, passes = 3) {
|
|
179
|
+
const r = Math.max(1, Math.round((Math.sqrt(4 * radius * radius + 1) - 1) / 2));
|
|
180
|
+
const imgData = ctx.getImageData(0, 0, w, h);
|
|
181
|
+
const s = imgData.data;
|
|
182
|
+
const d = new Uint8ClampedArray(s.length);
|
|
183
|
+
const diam = r * 2 + 1;
|
|
184
|
+
const inv = 1 / diam;
|
|
185
|
+
for (let pass = 0; pass < passes; pass++) {
|
|
186
|
+
for (let y = 0; y < h; y++) {
|
|
187
|
+
const row = y * w;
|
|
188
|
+
let rs = 0, gs = 0, bs = 0, as = 0;
|
|
189
|
+
for (let dx = -r; dx <= r; dx++) {
|
|
190
|
+
const i = row + Math.max(0, Math.min(w - 1, dx)) << 2;
|
|
191
|
+
rs += s[i];
|
|
192
|
+
gs += s[i | 1];
|
|
193
|
+
bs += s[i | 2];
|
|
194
|
+
as += s[i | 3];
|
|
195
|
+
}
|
|
196
|
+
for (let x = 0; x < w; x++) {
|
|
197
|
+
const o = row + x << 2;
|
|
198
|
+
d[o] = rs * inv;
|
|
199
|
+
d[o | 1] = gs * inv;
|
|
200
|
+
d[o | 2] = bs * inv;
|
|
201
|
+
d[o | 3] = as * inv;
|
|
202
|
+
const ai = row + Math.min(w - 1, x + r + 1) << 2;
|
|
203
|
+
const ri = row + Math.max(0, x - r) << 2;
|
|
204
|
+
rs += s[ai] - s[ri];
|
|
205
|
+
gs += s[ai | 1] - s[ri | 1];
|
|
206
|
+
bs += s[ai | 2] - s[ri | 2];
|
|
207
|
+
as += s[ai | 3] - s[ri | 3];
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
for (let x = 0; x < w; x++) {
|
|
211
|
+
let rs = 0, gs = 0, bs = 0, as = 0;
|
|
212
|
+
for (let dy = -r; dy <= r; dy++) {
|
|
213
|
+
const i = Math.max(0, Math.min(h - 1, dy)) * w + x << 2;
|
|
214
|
+
rs += d[i];
|
|
215
|
+
gs += d[i | 1];
|
|
216
|
+
bs += d[i | 2];
|
|
217
|
+
as += d[i | 3];
|
|
218
|
+
}
|
|
219
|
+
for (let y = 0; y < h; y++) {
|
|
220
|
+
const o = y * w + x << 2;
|
|
221
|
+
s[o] = rs * inv;
|
|
222
|
+
s[o | 1] = gs * inv;
|
|
223
|
+
s[o | 2] = bs * inv;
|
|
224
|
+
s[o | 3] = as * inv;
|
|
225
|
+
const ai = Math.min(h - 1, y + r + 1) * w + x << 2;
|
|
226
|
+
const ri = Math.max(0, y - r) * w + x << 2;
|
|
227
|
+
rs += d[ai] - d[ri];
|
|
228
|
+
gs += d[ai | 1] - d[ri | 1];
|
|
229
|
+
bs += d[ai | 2] - d[ri | 2];
|
|
230
|
+
as += d[ai | 3] - d[ri | 3];
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
ctx.putImageData(imgData, 0, 0);
|
|
235
|
+
}
|
|
236
|
+
function drawShape(ctx, path, size, tx, ty, rotate, scale, fillStyle, offsetX, offsetY) {
|
|
159
237
|
const cx = size / 2;
|
|
160
238
|
const cy = size / 2;
|
|
161
239
|
ctx.save();
|
|
@@ -174,11 +252,19 @@ function drawBlurredShape(ctx, path, size, tx, ty, rotate, scale, fillStyle, off
|
|
|
174
252
|
ctx.fill();
|
|
175
253
|
ctx.restore();
|
|
176
254
|
}
|
|
255
|
+
function getDevicePixelRatio() {
|
|
256
|
+
if (typeof window === "undefined" || !window.devicePixelRatio) return 1;
|
|
257
|
+
return Math.min(window.devicePixelRatio, 3);
|
|
258
|
+
}
|
|
177
259
|
function renderGradient(canvas, { size, colors, animated = false, seeds }) {
|
|
178
260
|
if (colors.length < 4) return null;
|
|
179
|
-
|
|
180
|
-
canvas.
|
|
261
|
+
const dpr = getDevicePixelRatio();
|
|
262
|
+
canvas.width = size * dpr;
|
|
263
|
+
canvas.height = size * dpr;
|
|
264
|
+
canvas.style.width = `${size}px`;
|
|
265
|
+
canvas.style.height = `${size}px`;
|
|
181
266
|
const ctx = canvas.getContext("2d");
|
|
267
|
+
ctx.scale(dpr, dpr);
|
|
182
268
|
const mix = seeds[0].toString() + seeds[1].toString() + seeds[2].toString() + seeds[3].toString();
|
|
183
269
|
const allSeeds = hashToSeeds(mix, 24);
|
|
184
270
|
const layers = [0, 1, 2, 3, 4, 5].map((i) => {
|
|
@@ -202,20 +288,24 @@ function renderGradient(canvas, { size, colors, animated = false, seeds }) {
|
|
|
202
288
|
];
|
|
203
289
|
const blur = Math.max(8, Math.round(size * 0.21));
|
|
204
290
|
const pad = Math.ceil(blur * 1.9);
|
|
291
|
+
const useFilter = hasFilterBlur();
|
|
205
292
|
const ROT_SPEEDS = [0.5, 0.6, 0.45, 0.55, 0.5, 0.65];
|
|
206
293
|
const DRIFT_AMP = size * 0.18;
|
|
207
294
|
const DRIFT_FREQS = [0.5, 0.45, 0.4, 0.48, 0.52, 0.38];
|
|
208
295
|
const DRIFT_PHASE_OFFSETS = [0, 1, 2, 0.5, 1.5, 3];
|
|
209
296
|
const PHASE_SPEED = 1.2;
|
|
297
|
+
const w = size + pad * 2;
|
|
298
|
+
const h = size + pad * 2;
|
|
299
|
+
const animResScale = !useFilter && animated ? 0.5 : 1;
|
|
300
|
+
const offCanvas = document.createElement("canvas");
|
|
301
|
+
offCanvas.width = w * dpr * animResScale;
|
|
302
|
+
offCanvas.height = h * dpr * animResScale;
|
|
303
|
+
const offCtx = offCanvas.getContext("2d");
|
|
304
|
+
offCtx.scale(dpr * animResScale, dpr * animResScale);
|
|
210
305
|
const draw = (phase2) => {
|
|
211
306
|
ctx.clearRect(0, 0, size, size);
|
|
212
307
|
ctx.fillStyle = hex0;
|
|
213
308
|
ctx.fillRect(0, 0, size, size);
|
|
214
|
-
const w = size + pad * 2;
|
|
215
|
-
const h = size + pad * 2;
|
|
216
|
-
const offCtx = document.createElement("canvas").getContext("2d");
|
|
217
|
-
offCtx.canvas.width = w;
|
|
218
|
-
offCtx.canvas.height = h;
|
|
219
309
|
for (let i = 0; i < 6; i++) {
|
|
220
310
|
const layer = layers[i];
|
|
221
311
|
const rot = layer.rotate + (animated ? phase2 * ROT_SPEEDS[i] : 0);
|
|
@@ -226,24 +316,22 @@ function renderGradient(canvas, { size, colors, animated = false, seeds }) {
|
|
|
226
316
|
const opts = LAYER_OPTS[i];
|
|
227
317
|
const hex = hexColors[i % 3];
|
|
228
318
|
offCtx.clearRect(0, 0, w, h);
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
ctx.drawImage(offCtx.canvas, 0, 0, w, h, -pad, -pad, w, h);
|
|
246
|
-
ctx.restore();
|
|
319
|
+
drawShape(offCtx, SHAPES[i], size, layer.tx + driftX, layer.ty + driftY, rot, scale, hex, pad, pad);
|
|
320
|
+
if (useFilter) {
|
|
321
|
+
ctx.save();
|
|
322
|
+
ctx.filter = `blur(${blur * dpr}px)`;
|
|
323
|
+
ctx.globalCompositeOperation = opts.composite;
|
|
324
|
+
ctx.globalAlpha = opts.alpha;
|
|
325
|
+
ctx.drawImage(offCanvas, 0, 0, offCanvas.width, offCanvas.height, -pad, -pad, w, h);
|
|
326
|
+
ctx.restore();
|
|
327
|
+
} else {
|
|
328
|
+
boxBlurCanvas(offCtx, offCanvas.width, offCanvas.height, blur * dpr * 1.4 * animResScale, animResScale < 1 ? 2 : 3);
|
|
329
|
+
ctx.save();
|
|
330
|
+
ctx.globalCompositeOperation = opts.composite;
|
|
331
|
+
ctx.globalAlpha = opts.alpha;
|
|
332
|
+
ctx.drawImage(offCanvas, 0, 0, offCanvas.width, offCanvas.height, -pad, -pad, w, h);
|
|
333
|
+
ctx.restore();
|
|
334
|
+
}
|
|
247
335
|
}
|
|
248
336
|
ctx.globalCompositeOperation = "source-over";
|
|
249
337
|
ctx.globalAlpha = 1;
|
|
@@ -283,27 +371,36 @@ function hexToRgb2(hex) {
|
|
|
283
371
|
const n = parseInt(hex.replace("#", ""), 16);
|
|
284
372
|
return { r: n >> 16 & 255, g: n >> 8 & 255, b: n & 255 };
|
|
285
373
|
}
|
|
374
|
+
function getDevicePixelRatio2() {
|
|
375
|
+
if (typeof window === "undefined" || !window.devicePixelRatio) return 1;
|
|
376
|
+
return Math.min(window.devicePixelRatio, 3);
|
|
377
|
+
}
|
|
286
378
|
function renderDither(canvas, { size, colors, dotScale: dotScaleOpt, animated = false, seeds }) {
|
|
287
|
-
|
|
288
|
-
|
|
379
|
+
const dpr = getDevicePixelRatio2();
|
|
380
|
+
const sizePx = Math.round(size * dpr);
|
|
381
|
+
canvas.width = sizePx;
|
|
382
|
+
canvas.height = sizePx;
|
|
383
|
+
canvas.style.width = `${size}px`;
|
|
384
|
+
canvas.style.height = `${size}px`;
|
|
289
385
|
const ctx = canvas.getContext("2d");
|
|
290
|
-
const
|
|
386
|
+
const dotScaleLogical = dotScaleOpt ?? Math.max(2, Math.round(size / 35));
|
|
387
|
+
const dotScale = Math.max(1, Math.round(dotScaleLogical * dpr));
|
|
291
388
|
const colA = hexToRgb2(oklchToHex(colors[0]));
|
|
292
389
|
const colB = hexToRgb2(oklchToHex(colors[Math.min(1, colors.length - 1)]));
|
|
293
390
|
const baseAngle = seeds[0] * Math.PI * 2;
|
|
294
391
|
const falloff = 0.55 + seeds[1] * 0.25;
|
|
295
392
|
const swirlSpeed = 0.45;
|
|
296
393
|
const padding = 1;
|
|
297
|
-
const gridSize = Math.ceil(
|
|
394
|
+
const gridSize = Math.ceil(sizePx / dotScale) + padding * 2;
|
|
298
395
|
const cellPhase = (gx, gy) => ((gx * 31 + gy * 17) * (seeds[2] * 1e3 + 1) + (seeds[3] * 1e3 | 0)) % 1e3 / 1e3 * Math.PI * 2;
|
|
299
396
|
const cellAmp = (gx, gy) => 0.035 + (gx * 7 + gy * 13 + seeds[2] * 50 | 0) % 55 / 1100;
|
|
300
397
|
const draw = (phase2) => {
|
|
301
|
-
const img = ctx.createImageData(
|
|
398
|
+
const img = ctx.createImageData(sizePx, sizePx);
|
|
302
399
|
const d = img.data;
|
|
303
400
|
const angle = baseAngle + (animated ? phase2 * swirlSpeed : 0);
|
|
304
401
|
const cosA = Math.cos(angle);
|
|
305
402
|
const sinA = Math.sin(angle);
|
|
306
|
-
for (let i = 0; i <
|
|
403
|
+
for (let i = 0; i < sizePx * sizePx * 4; i += 4) {
|
|
307
404
|
d[i] = colB.r;
|
|
308
405
|
d[i + 1] = colB.g;
|
|
309
406
|
d[i + 2] = colB.b;
|
|
@@ -329,8 +426,8 @@ function renderDither(canvas, { size, colors, dotScale: dotScaleOpt, animated =
|
|
|
329
426
|
for (let px = 0; px < dotScale; px++) {
|
|
330
427
|
const x = (gx - padding) * dotScale + px;
|
|
331
428
|
const y = (gy - padding) * dotScale + py;
|
|
332
|
-
if (x < 0 || y < 0 || x >=
|
|
333
|
-
const idx = (y *
|
|
429
|
+
if (x < 0 || y < 0 || x >= sizePx || y >= sizePx) continue;
|
|
430
|
+
const idx = (y * sizePx + x) * 4;
|
|
334
431
|
d[idx] = colA.r;
|
|
335
432
|
d[idx + 1] = colA.g;
|
|
336
433
|
d[idx + 2] = colA.b;
|
package/dist/index.js
CHANGED
|
@@ -121,7 +121,85 @@ var SHAPES = [
|
|
|
121
121
|
[[0.15, 0.2], [0.55, 0.08], [0.92, 0.35], [0.78, 0.75], [0.4, 0.92], [0.2, 0.6]],
|
|
122
122
|
[[0.45, 0.08], [0.82, 0.3], [0.7, 0.85], [0.3, 0.88], [0.08, 0.5], [0.25, 0.25]]
|
|
123
123
|
];
|
|
124
|
-
|
|
124
|
+
var _filterSupported = null;
|
|
125
|
+
function hasFilterBlur() {
|
|
126
|
+
if (_filterSupported !== null) return _filterSupported;
|
|
127
|
+
try {
|
|
128
|
+
const c = document.createElement("canvas");
|
|
129
|
+
c.width = 30;
|
|
130
|
+
c.height = 30;
|
|
131
|
+
const x = c.getContext("2d");
|
|
132
|
+
x.fillStyle = "#fff";
|
|
133
|
+
x.fillRect(0, 0, 30, 30);
|
|
134
|
+
x.filter = "blur(6px)";
|
|
135
|
+
x.fillStyle = "#000";
|
|
136
|
+
x.fillRect(12, 12, 4, 4);
|
|
137
|
+
_filterSupported = x.getImageData(22, 22, 1, 1).data[0] < 255;
|
|
138
|
+
x.filter = "none";
|
|
139
|
+
} catch {
|
|
140
|
+
_filterSupported = false;
|
|
141
|
+
}
|
|
142
|
+
return _filterSupported;
|
|
143
|
+
}
|
|
144
|
+
function boxBlurCanvas(ctx, w, h, radius, passes = 3) {
|
|
145
|
+
const r = Math.max(1, Math.round((Math.sqrt(4 * radius * radius + 1) - 1) / 2));
|
|
146
|
+
const imgData = ctx.getImageData(0, 0, w, h);
|
|
147
|
+
const s = imgData.data;
|
|
148
|
+
const d = new Uint8ClampedArray(s.length);
|
|
149
|
+
const diam = r * 2 + 1;
|
|
150
|
+
const inv = 1 / diam;
|
|
151
|
+
for (let pass = 0; pass < passes; pass++) {
|
|
152
|
+
for (let y = 0; y < h; y++) {
|
|
153
|
+
const row = y * w;
|
|
154
|
+
let rs = 0, gs = 0, bs = 0, as = 0;
|
|
155
|
+
for (let dx = -r; dx <= r; dx++) {
|
|
156
|
+
const i = row + Math.max(0, Math.min(w - 1, dx)) << 2;
|
|
157
|
+
rs += s[i];
|
|
158
|
+
gs += s[i | 1];
|
|
159
|
+
bs += s[i | 2];
|
|
160
|
+
as += s[i | 3];
|
|
161
|
+
}
|
|
162
|
+
for (let x = 0; x < w; x++) {
|
|
163
|
+
const o = row + x << 2;
|
|
164
|
+
d[o] = rs * inv;
|
|
165
|
+
d[o | 1] = gs * inv;
|
|
166
|
+
d[o | 2] = bs * inv;
|
|
167
|
+
d[o | 3] = as * inv;
|
|
168
|
+
const ai = row + Math.min(w - 1, x + r + 1) << 2;
|
|
169
|
+
const ri = row + Math.max(0, x - r) << 2;
|
|
170
|
+
rs += s[ai] - s[ri];
|
|
171
|
+
gs += s[ai | 1] - s[ri | 1];
|
|
172
|
+
bs += s[ai | 2] - s[ri | 2];
|
|
173
|
+
as += s[ai | 3] - s[ri | 3];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
for (let x = 0; x < w; x++) {
|
|
177
|
+
let rs = 0, gs = 0, bs = 0, as = 0;
|
|
178
|
+
for (let dy = -r; dy <= r; dy++) {
|
|
179
|
+
const i = Math.max(0, Math.min(h - 1, dy)) * w + x << 2;
|
|
180
|
+
rs += d[i];
|
|
181
|
+
gs += d[i | 1];
|
|
182
|
+
bs += d[i | 2];
|
|
183
|
+
as += d[i | 3];
|
|
184
|
+
}
|
|
185
|
+
for (let y = 0; y < h; y++) {
|
|
186
|
+
const o = y * w + x << 2;
|
|
187
|
+
s[o] = rs * inv;
|
|
188
|
+
s[o | 1] = gs * inv;
|
|
189
|
+
s[o | 2] = bs * inv;
|
|
190
|
+
s[o | 3] = as * inv;
|
|
191
|
+
const ai = Math.min(h - 1, y + r + 1) * w + x << 2;
|
|
192
|
+
const ri = Math.max(0, y - r) * w + x << 2;
|
|
193
|
+
rs += d[ai] - d[ri];
|
|
194
|
+
gs += d[ai | 1] - d[ri | 1];
|
|
195
|
+
bs += d[ai | 2] - d[ri | 2];
|
|
196
|
+
as += d[ai | 3] - d[ri | 3];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
ctx.putImageData(imgData, 0, 0);
|
|
201
|
+
}
|
|
202
|
+
function drawShape(ctx, path, size, tx, ty, rotate, scale, fillStyle, offsetX, offsetY) {
|
|
125
203
|
const cx = size / 2;
|
|
126
204
|
const cy = size / 2;
|
|
127
205
|
ctx.save();
|
|
@@ -140,11 +218,19 @@ function drawBlurredShape(ctx, path, size, tx, ty, rotate, scale, fillStyle, off
|
|
|
140
218
|
ctx.fill();
|
|
141
219
|
ctx.restore();
|
|
142
220
|
}
|
|
221
|
+
function getDevicePixelRatio() {
|
|
222
|
+
if (typeof window === "undefined" || !window.devicePixelRatio) return 1;
|
|
223
|
+
return Math.min(window.devicePixelRatio, 3);
|
|
224
|
+
}
|
|
143
225
|
function renderGradient(canvas, { size, colors, animated = false, seeds }) {
|
|
144
226
|
if (colors.length < 4) return null;
|
|
145
|
-
|
|
146
|
-
canvas.
|
|
227
|
+
const dpr = getDevicePixelRatio();
|
|
228
|
+
canvas.width = size * dpr;
|
|
229
|
+
canvas.height = size * dpr;
|
|
230
|
+
canvas.style.width = `${size}px`;
|
|
231
|
+
canvas.style.height = `${size}px`;
|
|
147
232
|
const ctx = canvas.getContext("2d");
|
|
233
|
+
ctx.scale(dpr, dpr);
|
|
148
234
|
const mix = seeds[0].toString() + seeds[1].toString() + seeds[2].toString() + seeds[3].toString();
|
|
149
235
|
const allSeeds = hashToSeeds(mix, 24);
|
|
150
236
|
const layers = [0, 1, 2, 3, 4, 5].map((i) => {
|
|
@@ -168,20 +254,24 @@ function renderGradient(canvas, { size, colors, animated = false, seeds }) {
|
|
|
168
254
|
];
|
|
169
255
|
const blur = Math.max(8, Math.round(size * 0.21));
|
|
170
256
|
const pad = Math.ceil(blur * 1.9);
|
|
257
|
+
const useFilter = hasFilterBlur();
|
|
171
258
|
const ROT_SPEEDS = [0.5, 0.6, 0.45, 0.55, 0.5, 0.65];
|
|
172
259
|
const DRIFT_AMP = size * 0.18;
|
|
173
260
|
const DRIFT_FREQS = [0.5, 0.45, 0.4, 0.48, 0.52, 0.38];
|
|
174
261
|
const DRIFT_PHASE_OFFSETS = [0, 1, 2, 0.5, 1.5, 3];
|
|
175
262
|
const PHASE_SPEED = 1.2;
|
|
263
|
+
const w = size + pad * 2;
|
|
264
|
+
const h = size + pad * 2;
|
|
265
|
+
const animResScale = !useFilter && animated ? 0.5 : 1;
|
|
266
|
+
const offCanvas = document.createElement("canvas");
|
|
267
|
+
offCanvas.width = w * dpr * animResScale;
|
|
268
|
+
offCanvas.height = h * dpr * animResScale;
|
|
269
|
+
const offCtx = offCanvas.getContext("2d");
|
|
270
|
+
offCtx.scale(dpr * animResScale, dpr * animResScale);
|
|
176
271
|
const draw = (phase2) => {
|
|
177
272
|
ctx.clearRect(0, 0, size, size);
|
|
178
273
|
ctx.fillStyle = hex0;
|
|
179
274
|
ctx.fillRect(0, 0, size, size);
|
|
180
|
-
const w = size + pad * 2;
|
|
181
|
-
const h = size + pad * 2;
|
|
182
|
-
const offCtx = document.createElement("canvas").getContext("2d");
|
|
183
|
-
offCtx.canvas.width = w;
|
|
184
|
-
offCtx.canvas.height = h;
|
|
185
275
|
for (let i = 0; i < 6; i++) {
|
|
186
276
|
const layer = layers[i];
|
|
187
277
|
const rot = layer.rotate + (animated ? phase2 * ROT_SPEEDS[i] : 0);
|
|
@@ -192,24 +282,22 @@ function renderGradient(canvas, { size, colors, animated = false, seeds }) {
|
|
|
192
282
|
const opts = LAYER_OPTS[i];
|
|
193
283
|
const hex = hexColors[i % 3];
|
|
194
284
|
offCtx.clearRect(0, 0, w, h);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
ctx.drawImage(offCtx.canvas, 0, 0, w, h, -pad, -pad, w, h);
|
|
212
|
-
ctx.restore();
|
|
285
|
+
drawShape(offCtx, SHAPES[i], size, layer.tx + driftX, layer.ty + driftY, rot, scale, hex, pad, pad);
|
|
286
|
+
if (useFilter) {
|
|
287
|
+
ctx.save();
|
|
288
|
+
ctx.filter = `blur(${blur * dpr}px)`;
|
|
289
|
+
ctx.globalCompositeOperation = opts.composite;
|
|
290
|
+
ctx.globalAlpha = opts.alpha;
|
|
291
|
+
ctx.drawImage(offCanvas, 0, 0, offCanvas.width, offCanvas.height, -pad, -pad, w, h);
|
|
292
|
+
ctx.restore();
|
|
293
|
+
} else {
|
|
294
|
+
boxBlurCanvas(offCtx, offCanvas.width, offCanvas.height, blur * dpr * 1.4 * animResScale, animResScale < 1 ? 2 : 3);
|
|
295
|
+
ctx.save();
|
|
296
|
+
ctx.globalCompositeOperation = opts.composite;
|
|
297
|
+
ctx.globalAlpha = opts.alpha;
|
|
298
|
+
ctx.drawImage(offCanvas, 0, 0, offCanvas.width, offCanvas.height, -pad, -pad, w, h);
|
|
299
|
+
ctx.restore();
|
|
300
|
+
}
|
|
213
301
|
}
|
|
214
302
|
ctx.globalCompositeOperation = "source-over";
|
|
215
303
|
ctx.globalAlpha = 1;
|
|
@@ -249,27 +337,36 @@ function hexToRgb2(hex) {
|
|
|
249
337
|
const n = parseInt(hex.replace("#", ""), 16);
|
|
250
338
|
return { r: n >> 16 & 255, g: n >> 8 & 255, b: n & 255 };
|
|
251
339
|
}
|
|
340
|
+
function getDevicePixelRatio2() {
|
|
341
|
+
if (typeof window === "undefined" || !window.devicePixelRatio) return 1;
|
|
342
|
+
return Math.min(window.devicePixelRatio, 3);
|
|
343
|
+
}
|
|
252
344
|
function renderDither(canvas, { size, colors, dotScale: dotScaleOpt, animated = false, seeds }) {
|
|
253
|
-
|
|
254
|
-
|
|
345
|
+
const dpr = getDevicePixelRatio2();
|
|
346
|
+
const sizePx = Math.round(size * dpr);
|
|
347
|
+
canvas.width = sizePx;
|
|
348
|
+
canvas.height = sizePx;
|
|
349
|
+
canvas.style.width = `${size}px`;
|
|
350
|
+
canvas.style.height = `${size}px`;
|
|
255
351
|
const ctx = canvas.getContext("2d");
|
|
256
|
-
const
|
|
352
|
+
const dotScaleLogical = dotScaleOpt ?? Math.max(2, Math.round(size / 35));
|
|
353
|
+
const dotScale = Math.max(1, Math.round(dotScaleLogical * dpr));
|
|
257
354
|
const colA = hexToRgb2(oklchToHex(colors[0]));
|
|
258
355
|
const colB = hexToRgb2(oklchToHex(colors[Math.min(1, colors.length - 1)]));
|
|
259
356
|
const baseAngle = seeds[0] * Math.PI * 2;
|
|
260
357
|
const falloff = 0.55 + seeds[1] * 0.25;
|
|
261
358
|
const swirlSpeed = 0.45;
|
|
262
359
|
const padding = 1;
|
|
263
|
-
const gridSize = Math.ceil(
|
|
360
|
+
const gridSize = Math.ceil(sizePx / dotScale) + padding * 2;
|
|
264
361
|
const cellPhase = (gx, gy) => ((gx * 31 + gy * 17) * (seeds[2] * 1e3 + 1) + (seeds[3] * 1e3 | 0)) % 1e3 / 1e3 * Math.PI * 2;
|
|
265
362
|
const cellAmp = (gx, gy) => 0.035 + (gx * 7 + gy * 13 + seeds[2] * 50 | 0) % 55 / 1100;
|
|
266
363
|
const draw = (phase2) => {
|
|
267
|
-
const img = ctx.createImageData(
|
|
364
|
+
const img = ctx.createImageData(sizePx, sizePx);
|
|
268
365
|
const d = img.data;
|
|
269
366
|
const angle = baseAngle + (animated ? phase2 * swirlSpeed : 0);
|
|
270
367
|
const cosA = Math.cos(angle);
|
|
271
368
|
const sinA = Math.sin(angle);
|
|
272
|
-
for (let i = 0; i <
|
|
369
|
+
for (let i = 0; i < sizePx * sizePx * 4; i += 4) {
|
|
273
370
|
d[i] = colB.r;
|
|
274
371
|
d[i + 1] = colB.g;
|
|
275
372
|
d[i + 2] = colB.b;
|
|
@@ -295,8 +392,8 @@ function renderDither(canvas, { size, colors, dotScale: dotScaleOpt, animated =
|
|
|
295
392
|
for (let px = 0; px < dotScale; px++) {
|
|
296
393
|
const x = (gx - padding) * dotScale + px;
|
|
297
394
|
const y = (gy - padding) * dotScale + py;
|
|
298
|
-
if (x < 0 || y < 0 || x >=
|
|
299
|
-
const idx = (y *
|
|
395
|
+
if (x < 0 || y < 0 || x >= sizePx || y >= sizePx) continue;
|
|
396
|
+
const idx = (y * sizePx + x) * 4;
|
|
300
397
|
d[idx] = colA.r;
|
|
301
398
|
d[idx + 1] = colA.g;
|
|
302
399
|
d[idx + 2] = colA.b;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hashvatar",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Deterministic avatar generation from any hash string. Zero dependencies.",
|
|
5
5
|
"author": "Médhy",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "tsup",
|
|
29
29
|
"dev": "tsup --watch",
|
|
30
|
-
"demo": "npx serve . -
|
|
30
|
+
"demo": "npx serve . -l tcp://0.0.0.0:5000",
|
|
31
31
|
"prepublishOnly": "npm run build"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
@@ -57,6 +57,6 @@
|
|
|
57
57
|
],
|
|
58
58
|
"repository": {
|
|
59
59
|
"type": "git",
|
|
60
|
-
"url": "https://github.com/medhychabour/hashvatar"
|
|
60
|
+
"url": "git+https://github.com/medhychabour/hashvatar.git"
|
|
61
61
|
}
|
|
62
62
|
}
|