Градиент ризографа с CSS Houdini - пример html js css



Книга Градиент ризографа с CSS Houdini



Градиент ризографа с CSS Houdini (HTML код)



<div class="risograph"></div>

<p class="warning" hidden>
Please use a browser with CSS Paint API enabled
<br>
(Chromium with <code>enable-experimental-web-platform-features</code> turned on in <code>chrome://flags</code>).
</p>

<script>
if ('paintWorklet' in CSS) {
CSS.paintWorklet.addModule('/chriskirknielsen/pen/wveMqxv.js');
}
else {
document.querySelector('.warning').hidden = false;
}
</script>



Градиент ризографа с CSS Houdini (CSS код)




@property --risograph-color {
syntax: "";
inherits: true;
initial-value: black;
}

@property --risograph-angle {
syntax: "";
inherits: true;
initial-value: 0deg;
}

@property --risograph-easing {
syntax: "";
inherits: true;
initial-value: easeInOutQuint;
}

:root { /* Update values here! */
--body-bg: #000000;
--risograph-color: #ffffff;
--risograph-angle: 45deg;
--risograph-easing: easeInOutQuint;
--demo-size: min(70vmin, 400px);
}

body {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 100vh;

color: #ffffff;
background: var(--body-bg, #000000);
}

.risograph {
/* Works best with a square */
width: var(--demo-size, 200px);
height: var(--demo-size, 200px);

border: 1px solid #808080;
background-image: linear-gradient(var(--risograph-angle, 0deg), var(--risograph-color, hotpink), 42%, transparent); /* Fallback */
}
@supports (background: paint(a)) {
.risograph {
background-image:
linear-gradient(var(--risograph-angle, 0deg), var(--risograph-color, hotpink) 2.5%, 20%, transparent 50%),
paint(risograph);
}
}

.warning {
padding: 2em;

text-align: center;
}


Градиент ризографа с CSS Houdini (JS код)




if (typeof registerPaint !== "undefined") {
/**
* Convert an angle to degrees.
* @param {String} angle Value of an angle in CSS syntax (e.g.: 90deg, -50rad, 10grad, -0.25turn)
* @return {Number} Angle value expressed in degrees
*/
function parseDegrees(angle) {
if (angle.toLowerCase().includes("grad")) {
return parseFloat(angle.replace("grad", "")) * 180 / 200;
}
if (angle.toLowerCase().includes("rad")) {
return parseFloat(angle.replace("rad", "")) * 180 / Math.PI;
}
if (angle.toLowerCase().includes("turn")) {
return parseFloat(angle.replace("turn", "")) * 360;
}
return parseFloat(angle.toString().replace("deg", ""));
}

/**
* Retrieve an angle values as degrees in the [0;360[ range
* @param {String} ang The angle value
* @return {Number} The degree value contained within the [0;360[ range
* */
function singleCycleDegrees(ang) {
const baseDeg = parseDegrees(ang) % 360;
const deg = baseDeg < 0 ? 360 - Math.abs(baseDeg) : baseDeg;
return deg;
}

/**
* Apply an easing to a value
* @source https://gist.github.com/gre/1650294
* @param {Number} value Number between 0 and 1
* @param {?String} type Easing type
* @return {Number} Eased value
*/
function easeValue(value, type = 'easeInOutQuint') {
const easingFunctions = {
// no easing, no acceleration
linear: t => t,
// accelerating from zero velocity
easeInQuad: t => t * t,
// decelerating to zero velocity
easeOutQuad: t => t * (2 - t),
// acceleration until halfway, then deceleration
easeInOutQuad: t => t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
// accelerating from zero velocity
easeInCubic: t => t * t * t,
// decelerating to zero velocity
easeOutCubic: t => --t * t * t + 1,
// acceleration until halfway, then deceleration
easeInOutCubic: t => t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
// accelerating from zero velocity
easeInQuart: t => t * t * t * t,
// decelerating to zero velocity
easeOutQuart: t => 1 - --t * t * t * t,
// acceleration until halfway, then deceleration
easeInOutQuart: t => t < .5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t,
// accelerating from zero velocity
easeInQuint: t => t * t * t * t * t,
// decelerating to zero velocity
easeOutQuint: t => 1 + --t * t * t * t * t,
// acceleration until halfway, then deceleration
easeInOutQuint: t => t < .5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t };

// If the provided type is invalid, use a default instead
if (typeof type !== 'string' || !easingFunctions.hasOwnProperty(type)) {type = 'easeInOutQuint';}
return easingFunctions[type](value);
}

/**
* Round a number to a specified decimal precision
* @param {Number} number The number to round
* @param {Number} precision Positive integer specifying the number of decimal places to show
* @return {Number} Rounded number
*/
function round(number, precision = 3) {
return parseFloat(number.toFixed(precision));
}

/**
* Clamp a number in a range
* @param {Number} number The number to clamp
* @param {Number} min The minimum value to return
* @param {Number} max The maximum value to return
* @return {Number} The clamped number
*/
function clamp(number, min = 0, max = 1) {
return Math.min(max, Math.max(min, number));
}

/**
* Remap a number from one boundary to another
* @param {Number} number Initial number to map to a new boundaries
* @param {Number} in_min The initial number's lower boundary
* @param {Number} in_max The initial number's upper boundary
* @param {Number} out_min The final number's lower boundary
* @param {Number} out_max The final number's upper boundary
* @return {Number} Remapped number
*/
function remapNumber(number, in_min, in_max, out_min, out_max) {
return (number - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

registerPaint("risograph", class {
static get inputProperties() {
return ['--risograph-color', '--risograph-angle', '--risograph-easing'];
}

paint(ctx, geometry, properties) {
const { width, height } = geometry;
const color = properties.get('--risograph-color').toString().trim();
const angle = properties.get('--risograph-angle').toString().trim();
const easing = properties.get('--risograph-easing').toString().trim() || 'easeOutQuint';

// Define coordinate system
const deg = singleCycleDegrees(angle);
const xMin = 0;
const xMax = width;
const yMin = 0;
const yMax = height;
const lengthMin = Math.min(xMax, yMax);
const lengthMax = Math.max(xMax, yMax);
const lengthRatio = lengthMax / lengthMin;
const modAngle = deg % 90;
const isStraightAngle = modAngle === 0;
const angleDelta = 45 + Math.abs(-45 + modAngle) * -1; // `45 + |-45 + α| * -1` gives us the max scaling at 45 and goes back down after that value (triangle peaks at 45, is between [0;90]) — plot it here: https://www.desmos.com/calculator
const scale = remapNumber(angleDelta, 0, 45, 1, Math.sqrt(2) * lengthRatio); // sqrt(2) is the ratio of the diagonal inside a square copared to the side
const pxSize = !isStraightAngle ? remapNumber(angleDelta, 0, 45, 1, 1.0875) : 1;

// Define the color for all operations
ctx.fillStyle = color;

// Rotate the canvas by the required angle
ctx.translate(width * 0.5, height * 0.5);
if (!isStraightAngle) {ctx.scale(scale, scale);} // If the angle is not straight, scale up the canvas to cover the edges
ctx.rotate(deg * Math.PI / 180);
ctx.translate(-0.5 * width, -0.5 * height);

for (let y = yMin; y < lengthMax; y++) {if (window.CP.shouldStopExecution(0)) break;
for (let x = xMin; x < lengthMax; x++) {if (window.CP.shouldStopExecution(1)) break;
const ratio = y / yMax;
const randomFactor = clamp(Math.random() + 0.001, 0, 1);
const alpha = round(easeValue(ratio * randomFactor, easing), 3);

ctx.globalAlpha = alpha;
ctx.fillRect(x, y, pxSize, pxSize);
}window.CP.exitedLoop(1);
}

// Reset the transforms
window.CP.exitedLoop(0);ctx.translate(width * 0.5, height * 0.5);
ctx.rotate(-1 * deg * Math.PI / 180);
ctx.translate(-0.5 * width, -0.5 * height);
ctx.scale(1, 1);
}});

}
//# sourceURL=pen.js


Градиент ризографа с CSS Houdini (Результат кода)


595   0  

Comments

    Ничего не найдено.