Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion public/r/Shuffle-JS-CSS.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/r/Shuffle-JS-TW.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/r/Shuffle-TS-CSS.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/r/Shuffle-TS-TW.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/r/WarpText-JS-CSS.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/r/WarpText-JS-TW.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/r/WarpText-TS-CSS.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/r/WarpText-TS-TW.json

Large diffs are not rendered by default.

82 changes: 75 additions & 7 deletions src/content/TextAnimations/Shuffle/Shuffle.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,56 @@ const Shuffle = ({

const rolls = Math.max(1, Math.floor(shuffleTimes));
const rand = set => set.charAt(Math.floor(Math.random() * set.length)) || '';
const isVertical = shuffleDirection === 'up' || shuffleDirection === 'down';
const metricsContext = isVertical ? document.createElement('canvas').getContext('2d') : null;

const measureVerticalCell = (node, lineBoxHeight) => {
const computed = window.getComputedStyle(node);
let fontHeight = 0;

if (metricsContext) {
metricsContext.font = [
computed.fontStyle,
computed.fontVariant,
computed.fontWeight,
computed.fontSize,
computed.fontFamily
].join(' ');

const sample = `${node.textContent || 'M'}${scrambleCharset}`;
const metrics = metricsContext.measureText(sample);
const ascent = metrics.fontBoundingBoxAscent;
const descent = metrics.fontBoundingBoxDescent;
if (Number.isFinite(ascent) && Number.isFinite(descent)) fontHeight = ascent + descent;
}

if (!fontHeight) {
const probe = node.cloneNode(true);
probe.textContent = `${node.textContent || 'M'}${scrambleCharset}`;
Object.assign(probe.style, {
position: 'absolute',
visibility: 'hidden',
pointerEvents: 'none',
width: 'auto',
height: 'auto',
whiteSpace: 'nowrap',
lineHeight: 'normal',
fontFamily: computed.fontFamily,
fontSize: computed.fontSize,
fontStyle: computed.fontStyle,
fontVariant: computed.fontVariant,
fontWeight: computed.fontWeight,
fontStretch: computed.fontStretch
});
document.body.appendChild(probe);
fontHeight = probe.getBoundingClientRect().height;
probe.remove();
}

const overflow = Math.max(0, Math.ceil(fontHeight - lineBoxHeight));
const padTop = Math.floor(overflow / 2);
return { cellHeight: lineBoxHeight + overflow, padTop, padBottom: overflow - padTop };
};

chars.forEach(ch => {
const parent = ch.parentElement;
Expand All @@ -126,19 +176,25 @@ const Shuffle = ({
const h = ch.getBoundingClientRect().height;
if (!w) return;

const { cellHeight, padTop, padBottom } = isVertical
? measureVerticalCell(ch, h)
: { cellHeight: h, padTop: 0, padBottom: 0 };

const wrap = document.createElement('span');
Object.assign(wrap.style, {
display: 'inline-block',
overflow: 'hidden',
width: w + 'px',
height: shuffleDirection === 'up' || shuffleDirection === 'down' ? h + 'px' : 'auto',
height: isVertical ? cellHeight + 'px' : 'auto',
marginTop: isVertical ? -padTop + 'px' : '0',
marginBottom: isVertical ? -padBottom + 'px' : '0',
verticalAlign: 'bottom'
});

const inner = document.createElement('span');
Object.assign(inner.style, {
display: 'inline-block',
whiteSpace: shuffleDirection === 'up' || shuffleDirection === 'down' ? 'normal' : 'nowrap',
whiteSpace: isVertical ? 'normal' : 'nowrap',
willChange: 'transform'
});

Expand All @@ -147,14 +203,22 @@ const Shuffle = ({

const firstOrig = ch.cloneNode(true);
Object.assign(firstOrig.style, {
display: shuffleDirection === 'up' || shuffleDirection === 'down' ? 'block' : 'inline-block',
display: isVertical ? 'flex' : 'inline-block',
alignItems: isVertical ? 'center' : '',
justifyContent: isVertical ? 'center' : '',
height: isVertical ? cellHeight + 'px' : '',
lineHeight: isVertical ? h + 'px' : '',
width: w + 'px',
textAlign: 'center'
});

ch.setAttribute('data-orig', '1');
Object.assign(ch.style, {
display: shuffleDirection === 'up' || shuffleDirection === 'down' ? 'block' : 'inline-block',
display: isVertical ? 'flex' : 'inline-block',
alignItems: isVertical ? 'center' : '',
justifyContent: isVertical ? 'center' : '',
height: isVertical ? cellHeight + 'px' : '',
lineHeight: isVertical ? h + 'px' : '',
width: w + 'px',
textAlign: 'center'
});
Expand All @@ -164,7 +228,11 @@ const Shuffle = ({
const c = ch.cloneNode(true);
if (scrambleCharset) c.textContent = rand(scrambleCharset);
Object.assign(c.style, {
display: shuffleDirection === 'up' || shuffleDirection === 'down' ? 'block' : 'inline-block',
display: isVertical ? 'flex' : 'inline-block',
alignItems: isVertical ? 'center' : '',
justifyContent: isVertical ? 'center' : '',
height: isVertical ? cellHeight + 'px' : '',
lineHeight: isVertical ? h + 'px' : '',
width: w + 'px',
textAlign: 'center'
});
Expand Down Expand Up @@ -193,11 +261,11 @@ const Shuffle = ({
startX = 0;
finalX = -steps * w;
} else if (shuffleDirection === 'down') {
startY = -steps * h;
startY = -steps * cellHeight;
finalY = 0;
} else if (shuffleDirection === 'up') {
startY = 0;
finalY = -steps * h;
finalY = -steps * cellHeight;
}

if (shuffleDirection === 'left' || shuffleDirection === 'right') {
Expand Down
8 changes: 6 additions & 2 deletions src/content/TextAnimations/WarpText/WarpText.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,9 @@ const WarpText = ({
if (document.fonts?.ready) {
try {
await document.fonts.ready;
} catch {}
} catch (error) {
void error;
}
}
if (disposed || contextLost || version !== rasterVersion) return;

Expand Down Expand Up @@ -509,7 +511,9 @@ const WarpText = ({
geometry?.remove?.();
program?.remove?.();
gl.getExtension('WEBGL_lose_context')?.loseContext();
} catch {}
} catch (error) {
void error;
}
}

if (canvas.parentNode === container) container.removeChild(canvas);
Expand Down
100 changes: 87 additions & 13 deletions src/tailwind/TextAnimations/Shuffle/Shuffle.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,56 @@ const Shuffle = ({

const rolls = Math.max(1, Math.floor(shuffleTimes));
const rand = set => set.charAt(Math.floor(Math.random() * set.length)) || '';
const isVertical = shuffleDirection === 'up' || shuffleDirection === 'down';
const metricsContext = isVertical ? document.createElement('canvas').getContext('2d') : null;

const measureVerticalCell = (node, lineBoxHeight) => {
const computed = window.getComputedStyle(node);
let fontHeight = 0;

if (metricsContext) {
metricsContext.font = [
computed.fontStyle,
computed.fontVariant,
computed.fontWeight,
computed.fontSize,
computed.fontFamily
].join(' ');

const sample = `${node.textContent || 'M'}${scrambleCharset}`;
const metrics = metricsContext.measureText(sample);
const ascent = metrics.fontBoundingBoxAscent;
const descent = metrics.fontBoundingBoxDescent;
if (Number.isFinite(ascent) && Number.isFinite(descent)) fontHeight = ascent + descent;
}

if (!fontHeight) {
const probe = node.cloneNode(true);
probe.textContent = `${node.textContent || 'M'}${scrambleCharset}`;
Object.assign(probe.style, {
position: 'absolute',
visibility: 'hidden',
pointerEvents: 'none',
width: 'auto',
height: 'auto',
whiteSpace: 'nowrap',
lineHeight: 'normal',
fontFamily: computed.fontFamily,
fontSize: computed.fontSize,
fontStyle: computed.fontStyle,
fontVariant: computed.fontVariant,
fontWeight: computed.fontWeight,
fontStretch: computed.fontStretch
});
document.body.appendChild(probe);
fontHeight = probe.getBoundingClientRect().height;
probe.remove();
}

const overflow = Math.max(0, Math.ceil(fontHeight - lineBoxHeight));
const padTop = Math.floor(overflow / 2);
return { cellHeight: lineBoxHeight + overflow, padTop, padBottom: overflow - padTop };
};

chars.forEach(ch => {
const parent = ch.parentElement;
Expand All @@ -137,39 +187,63 @@ const Shuffle = ({
const h = ch.getBoundingClientRect().height;
if (!w) return;

const { cellHeight, padTop, padBottom } = isVertical
? measureVerticalCell(ch, h)
: { cellHeight: h, padTop: 0, padBottom: 0 };

const wrap = document.createElement('span');
wrap.className = 'inline-block overflow-hidden text-left';
Object.assign(wrap.style, {
width: w + 'px',
height: shuffleDirection === 'up' || shuffleDirection === 'down' ? h + 'px' : 'auto',
height: isVertical ? cellHeight + 'px' : 'auto',
marginTop: isVertical ? -padTop + 'px' : '0',
marginBottom: isVertical ? -padBottom + 'px' : '0',
verticalAlign: 'bottom'
});

const inner = document.createElement('span');
inner.className =
'inline-block will-change-transform origin-left transform-gpu ' +
(shuffleDirection === 'up' || shuffleDirection === 'down' ? 'whitespace-normal' : 'whitespace-nowrap');
(isVertical ? 'whitespace-normal' : 'whitespace-nowrap');

parent.insertBefore(wrap, ch);
wrap.appendChild(inner);

const firstOrig = ch.cloneNode(true);
firstOrig.className =
'text-left ' + (shuffleDirection === 'up' || shuffleDirection === 'down' ? 'block' : 'inline-block');
Object.assign(firstOrig.style, { width: w + 'px', fontFamily: computedFont });
firstOrig.className = 'text-left ' + (isVertical ? 'flex' : 'inline-block');
Object.assign(firstOrig.style, {
alignItems: isVertical ? 'center' : '',
justifyContent: isVertical ? 'center' : '',
height: isVertical ? cellHeight + 'px' : '',
lineHeight: isVertical ? h + 'px' : '',
width: w + 'px',
fontFamily: computedFont
});

ch.setAttribute('data-orig', '1');
ch.className =
'text-left ' + (shuffleDirection === 'up' || shuffleDirection === 'down' ? 'block' : 'inline-block');
Object.assign(ch.style, { width: w + 'px', fontFamily: computedFont });
ch.className = 'text-left ' + (isVertical ? 'flex' : 'inline-block');
Object.assign(ch.style, {
alignItems: isVertical ? 'center' : '',
justifyContent: isVertical ? 'center' : '',
height: isVertical ? cellHeight + 'px' : '',
lineHeight: isVertical ? h + 'px' : '',
width: w + 'px',
fontFamily: computedFont
});

inner.appendChild(firstOrig);
for (let k = 0; k < rolls; k++) {
const c = ch.cloneNode(true);
if (scrambleCharset) c.textContent = rand(scrambleCharset);
c.className =
'text-left ' + (shuffleDirection === 'up' || shuffleDirection === 'down' ? 'block' : 'inline-block');
Object.assign(c.style, { width: w + 'px', fontFamily: computedFont });
c.className = 'text-left ' + (isVertical ? 'flex' : 'inline-block');
Object.assign(c.style, {
alignItems: isVertical ? 'center' : '',
justifyContent: isVertical ? 'center' : '',
height: isVertical ? cellHeight + 'px' : '',
lineHeight: isVertical ? h + 'px' : '',
width: w + 'px',
fontFamily: computedFont
});
inner.appendChild(c);
}
inner.appendChild(ch);
Expand All @@ -195,11 +269,11 @@ const Shuffle = ({
startX = 0;
finalX = -steps * w;
} else if (shuffleDirection === 'down') {
startY = -steps * h;
startY = -steps * cellHeight;
finalY = 0;
} else if (shuffleDirection === 'up') {
startY = 0;
finalY = -steps * h;
finalY = -steps * cellHeight;
}

if (shuffleDirection === 'left' || shuffleDirection === 'right') {
Expand Down
8 changes: 6 additions & 2 deletions src/tailwind/TextAnimations/WarpText/WarpText.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,9 @@ const WarpText = ({
if (document.fonts?.ready) {
try {
await document.fonts.ready;
} catch {}
} catch (error) {
void error;
}
}
if (disposed || contextLost || version !== rasterVersion) return;

Expand Down Expand Up @@ -508,7 +510,9 @@ const WarpText = ({
geometry?.remove?.();
program?.remove?.();
gl.getExtension('WEBGL_lose_context')?.loseContext();
} catch {}
} catch (error) {
void error;
}
}

if (canvas.parentNode === container) container.removeChild(canvas);
Expand Down
Loading