From cd92eeb7c8b66304ccd77637abdc0d4493645c1d Mon Sep 17 00:00:00 2001 From: Alex Lende Date: Thu, 21 Dec 2023 11:50:31 -0600 Subject: [PATCH] Fix starscape when plugin isn't active (#341) --- blocks/starscape/src/deprecated/index.js | 3 +- .../starscape/src/deprecated/v2/attributes.js | 55 +++++ blocks/starscape/src/deprecated/v2/index.js | 39 ++++ blocks/starscape/src/deprecated/v2/save.js | 49 ++++ .../starscape/src/deprecated/v2/starscape.js | 52 +++++ blocks/starscape/src/deprecated/v2/utils.js | 215 ++++++++++++++++++ blocks/starscape/src/save.js | 2 +- 7 files changed, 413 insertions(+), 2 deletions(-) create mode 100644 blocks/starscape/src/deprecated/v2/attributes.js create mode 100644 blocks/starscape/src/deprecated/v2/index.js create mode 100644 blocks/starscape/src/deprecated/v2/save.js create mode 100644 blocks/starscape/src/deprecated/v2/starscape.js create mode 100644 blocks/starscape/src/deprecated/v2/utils.js diff --git a/blocks/starscape/src/deprecated/index.js b/blocks/starscape/src/deprecated/index.js index 5060b60d..a6c9c66a 100644 --- a/blocks/starscape/src/deprecated/index.js +++ b/blocks/starscape/src/deprecated/index.js @@ -1,6 +1,7 @@ import v1 from './v1'; +import v2 from './v2'; // Deprecations should run in reverse chronological order. Most probable // deprecations to run are the most recent. This ordering makes the process // a little more performant. -export default [ v1 ]; +export default [ v2, v1 ]; diff --git a/blocks/starscape/src/deprecated/v2/attributes.js b/blocks/starscape/src/deprecated/v2/attributes.js new file mode 100644 index 00000000..0a768926 --- /dev/null +++ b/blocks/starscape/src/deprecated/v2/attributes.js @@ -0,0 +1,55 @@ +export default { + align: { + type: 'string', + default: 'full', + }, + color: { + type: 'string', + default: '#fff', + }, + background: { + type: 'string', + // Default #000 in style.scss. + }, + intensity: { + type: 'number', + default: 80, + }, + density: { + type: 'number', + default: 20, + }, + speed: { + type: 'number', + default: 20, + }, + areaWidth: { + type: 'integer', + default: 1920, + }, + areaHeight: { + type: 'integer', + default: 1080, + }, + minHeight: { + type: 'string', + // Default 430px in style.scss. + }, + layout: { + type: 'object', + default: { + type: 'constrained', + }, + }, + tagName: { + type: 'string', + default: 'div', + }, + templateLock: { + type: [ 'string', 'boolean' ], + enum: [ 'all', 'insert', 'contentOnly', false ], + }, + allowedBlocks: { + type: 'array', + }, +}; diff --git a/blocks/starscape/src/deprecated/v2/index.js b/blocks/starscape/src/deprecated/v2/index.js new file mode 100644 index 00000000..4cb5a4ad --- /dev/null +++ b/blocks/starscape/src/deprecated/v2/index.js @@ -0,0 +1,39 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import attributes from './attributes'; +import save from './save'; + +export default { + supports: { + className: true, + align: [ 'wide', 'full' ], + color: { + heading: true, + text: true, + link: true, + background: false, + gradients: false, + }, + html: false, + layout: { + allowJustification: false, + }, + spacing: { + padding: true, + margin: [ 'top', 'bottom' ], + blockGap: true, + __experimentalDefaultControls: { + padding: true, + blockGap: true, + }, + }, + }, + attributes, + save, +}; diff --git a/blocks/starscape/src/deprecated/v2/save.js b/blocks/starscape/src/deprecated/v2/save.js new file mode 100644 index 00000000..5380a040 --- /dev/null +++ b/blocks/starscape/src/deprecated/v2/save.js @@ -0,0 +1,49 @@ +/** + * WordPress dependencies + */ +import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import Starscape from './starscape'; +import { genStars, genAnimations } from './utils'; + +function StarscapeSave( { attributes } ) { + const { + background, + color, + intensity, + density, + speed, + minHeight, + areaWidth, + areaHeight, + tagName, + } = attributes; + + const starStyles = genStars( { color, density, areaWidth, areaHeight } ); + const animationStyles = genAnimations( { speed } ); + + const blockProps = useBlockProps.save( { + style: { background, minHeight }, + } ); + + const innerBlocksProps = useInnerBlocksProps.save( { + className: 'wp-block-a8c-starscape__inner', + } ); + + return ( + +
+ + ); +} + +export default StarscapeSave; diff --git a/blocks/starscape/src/deprecated/v2/starscape.js b/blocks/starscape/src/deprecated/v2/starscape.js new file mode 100644 index 00000000..73af5498 --- /dev/null +++ b/blocks/starscape/src/deprecated/v2/starscape.js @@ -0,0 +1,52 @@ +/** + * WordPress dependencies + */ +import { forwardRef } from '@wordpress/element'; + +/** + * @typedef {Object} StarscapeProps + * @property {string} [as] HTML tag name. + * @property {Object} starStyles Styles for the stars. + * @property {Object} animationStyles Styles for the animation. + * @property {number} intensity Intensity of the stars. + * @property {WPElement} [children] Children of the component. + */ + +/** + * Starscape background effect component. + * + * @param {StarscapeProps} props Starscape props + * @param {Ref} ref React ref + * + * @returns {WPElement} Starscape component + */ +const Starscape = ( + { + as: Component = 'div', + starStyles, + animationStyles, + intensity, + children, + ...props + }, + ref +) => { + return ( + + { [ 0, 1, 2 ].map( ( i ) => ( +
+ ) ) } + { children } + + ); +}; + +export default forwardRef( Starscape ); diff --git a/blocks/starscape/src/deprecated/v2/utils.js b/blocks/starscape/src/deprecated/v2/utils.js new file mode 100644 index 00000000..6289613f --- /dev/null +++ b/blocks/starscape/src/deprecated/v2/utils.js @@ -0,0 +1,215 @@ +/** + * External dependencies + */ +import { colord, extend } from 'colord'; +import namesPlugin from 'colord/plugins/names'; +import minifierPlugin from 'colord/plugins/minify'; + +extend( [ namesPlugin, minifierPlugin ] ); + +/** + * Generate pseudo-random numbers from a seed using an LCG algorithm. + * + * LCG values selected from L'Ecuyer, Pierre Math. Comp. 68 (1999), 249-260. + * @see {@link https://doi.org/10.1090%2FS0025-5718-99-00996-5} + * + * @param {number} seed Seed value (will be converted to 23-bit integer) + * + * @return {Generator} Generator of random numbers between 0 and 1 + */ +function* seededRandoms( seed ) { + const modulus = 0x7fffffff; + seed = ( seed >>> 0 ) % modulus; + while ( true ) { + seed = Math.imul( seed, 0x0034e7f7 ) % modulus; + yield ( seed & modulus ) / modulus; + } +} + +/** + * 32-bit FNV hash function. + * + * @param {ArrayBuffer} buffer Data to hash + * + * @return {number} 32-bit hash for the array + */ +function hashUint32( buffer ) { + return new Uint32Array( buffer ).reduce( + ( hash, data ) => Math.imul( hash, 0x01000193 ) ^ data, + 0x811c9dc5 + ); +} + +/** + * Generates a unique hash for the star box shadow arguments. + * + * @param {string} color Color option + * @param {number} radius Radius option + * @param {number} count Count option + * + * @return {number} 32-bit hash for the arguments + */ +function hashStarBoxShadowArgs( count, radius, color ) { + const colorData = new TextEncoder().encode( color ); + const buffer = new ArrayBuffer( + // 2 64-bit floats + color string + padding to 4-byte uint32 boundary. + 16 + colorData.byteLength + ( -colorData.byteLength & 3 ) + ); + new Float64Array( buffer, 0, 2 ).set( [ count, radius ] ); + new Uint8Array( buffer, 16 ).set( colorData ); + return hashUint32( buffer ); +} + +/** + * Calculates points randomly within a circle so we don't bother calculating + * for the points that will never appear on screen. + * + * @param {number} radius radius to randomly generate in + * + * @return {Object} Random { x, y } coordinates + */ +const randomPoint = ( radius, prng ) => { + const a = prng.next().value * 2 * Math.PI; + const r = radius * Math.sqrt( prng.next().value ); + + return { + x: r * Math.cos( a ), + y: r * Math.sin( a ), + }; +}; + +/** + * Calculate the box shadow. Optimized for shortest string length. + * + * @param {Object} options Options + * @param {string} options.color CSS Color string + * @param {number} options.density Quantity of stars in stars per million pixels + * @param {number} options.areaWidth Max width of the area to fill in pixels + * @param {number} options.areaHeight Max height of the area to fill in pixels + */ +const starBoxShadow = ( { color, density, areaWidth, areaHeight } ) => { + // Any points outside the circle covering the rectangle, centered on the + // midpoint of the bottom edge, won't be shown, so don't bother placing + // any there. + const base = areaWidth / 2; + const height = areaHeight; + const radius = Math.sqrt( base * base + height * height ); + + // Star count calculation based on how dense the stars should be distributed + // within our minimal circle + const area = Math.PI * radius * radius; + const count = Math.floor( area * density * 1e-6 ); + + // Custom prng for deterministic results. + const seed = hashStarBoxShadowArgs( count, radius, color ); + const prng = seededRandoms( seed ); + + return Array.from( { length: count }, () => { + const { x, y } = randomPoint( radius, prng ); + // Rounding to the nearest pixel saves up to 18% on string length + return `${ Math.round( x ) }px ${ Math.round( y ) }px ${ color }`; + } ).join( ',' ); +}; + +/** + * Calculates the style object in JS for the starscape stars. + * + * @param {Object} options Options + * @param {number} options.starSize Size of star in pixels + * @param {string} options.color CSS Color string + * @param {number} options.density Value between 0 and 1 + * @param {number} options.areaWidth Maximum width the container may be in pixels + * @param {number} options.areaHeight Maximum height the container may be in pixels + * + * @return {Object} CSS style object to add to a component + */ +const starBoxStyle = ( { + starSize, + color, + density, + areaWidth, + areaHeight, +} ) => ( { + boxShadow: starBoxShadow( { color, density, areaWidth, areaHeight } ), + width: `${ starSize }px`, + height: `${ starSize }px`, +} ); + +/** + * Calculates the style object in JS for the starscape animation. + * + * @param {Object} options Options + * @param {string} options.animation Type of animation (currently just `rotate`) + * @param {number} options.duration Duration of animation in seconds + * + * @return {Object} CSS style object to add to a component + */ +const starBoxAnimation = ( { animation, duration } ) => ( { + animation: `wp-block-a8c-starscape-animation-${ animation } ${ duration }s linear infinite`, +} ); + +/** + * Calculate all three sizes of stars for saving in the attributes. + * + * @param {Object} options Options + * @param {string} options.color CSS Color string for the stars + * @param {number} options.density Range slider value between 1 and 100 + * @param {number} options.areaWidth Width in pixels that stars should be generated within + * @param {number} options.areaHeight Height in pixels that stars should be generated within + * + * @return {Object[]} CSS style objects for each layer of stars + */ +export const genStars = ( { + color: _color, + density, + areaWidth, + areaHeight, +} ) => { + // A little bit of minification goes a long way here since the color of each + // individual star needs to be specified. + const color = colord( _color ).minify( { + hex: true, + alphaHex: true, + rgb: true, + hsl: true, + name: true, + transparent: true, + } ); + return [ + { + starSize: 1, + density: 4 * density + 10, + }, + { + starSize: 2, + density: 2 * density + 10, + }, + { + starSize: 3, + density: 1 * density + 10, + }, + ].map( ( variation ) => + starBoxStyle( { + color, + areaWidth, + areaHeight, + ...variation, + } ) + ); +}; + +/** + * Calculate all three sizes of stars for saving in the attributes. + * + * @param {Object} options Options + * @param {number} options.speed Value between 1 and 100 + * + * @return {Object[]} CSS style objects for each layer of stars + */ +export const genAnimations = ( { speed } ) => + [ 50000, 100000, 200000 ].map( ( duration ) => + starBoxAnimation( { + animation: 'rotate', + duration: duration / ( speed * speed ), + } ) + ); diff --git a/blocks/starscape/src/save.js b/blocks/starscape/src/save.js index 5380a040..a35e4264 100644 --- a/blocks/starscape/src/save.js +++ b/blocks/starscape/src/save.js @@ -26,7 +26,7 @@ function StarscapeSave( { attributes } ) { const animationStyles = genAnimations( { speed } ); const blockProps = useBlockProps.save( { - style: { background, minHeight }, + style: { background, minHeight, overflow: 'hidden' }, } ); const innerBlocksProps = useInnerBlocksProps.save( {