import GUI from '../../dist/lil-gui.esm.js';
import './BezierController.js';
import Bezier from 'https://cdn.skypack.dev/cubic-bezier-easing@1.0';
const params = {
showPreview: true,
curve: [ .85, .05, .10, .95 ],
duration: 2.5,
};
const gui = new GUI();
const preview = document.getElementById( 'curve-preview' );
let easing;
function updateCurve() {
easing = new Bezier( ...params.curve );
}
updateCurve();
gui.addBezier( params, 'curve' ).onChange( updateCurve );
gui.add( params, 'duration', 0.5, 5 );
gui.add( params, 'showPreview' )
.onChange( v => {
preview.style.visibility = v ? '' : 'hidden';
} );
function animate() {
requestAnimationFrame( animate );
const time = Date.now() / ( params.duration * 1000 );
let val = easing( time % 1 );
if ( Math.floor( time ) % 2 == 0 ) val -= 1;
preview.style.transform = `translateX( ${val * 100}% )`;
}
animate();
import { Controller, GUI, injectStyles } from '../../dist/lil-gui.esm.js';
export default class BezierController extends Controller {
constructor( parent, object, property ) {
super( parent, object, property, 'bezier' );
const svgNS = 'http://www.w3.org/2000/svg';
this.$svg = document.createElementNS( svgNS, 'svg' );
this.$path = document.createElementNS( svgNS, 'path' );
this.$line1 = document.createElementNS( svgNS, 'line' );
this.$line2 = document.createElementNS( svgNS, 'line' );
this.$knob1 = document.createElementNS( svgNS, 'circle' );
this.$knob2 = document.createElementNS( svgNS, 'circle' );
this.$svg.setAttribute( 'viewBox', '0 0 1 1' );
this.$knob1.setAttribute( 'r', 0.05 );
this.$knob2.setAttribute( 'r', 0.05 );
this.$line1.setAttribute( 'x1', 0 );
this.$line1.setAttribute( 'y1', 0 );
this.$line2.setAttribute( 'x1', 1 );
this.$line2.setAttribute( 'y1', 1 );
this.$svg.appendChild( this.$line1 );
this.$svg.appendChild( this.$line2 );
this.$svg.appendChild( this.$path );
this.$svg.appendChild( this.$knob1 );
this.$svg.appendChild( this.$knob2 );
this.$widget.appendChild( this.$svg );
const makeDraggable = ( knob, setter ) => {
const clamp = x => Math.max( 0, Math.min( 1, x ) );
const inverseLerp = ( t, a, b ) => ( t - a ) / ( b - a );
knob.addEventListener( 'mousedown', () => {
window.addEventListener( 'mousemove', onMouseMove );
window.addEventListener( 'mouseup', onMouseUp );
} );
const onMouseMove = e => {
e.preventDefault();
const rect = this.$svg.getBoundingClientRect();
const x = inverseLerp( e.clientX, rect.left, rect.right );
const y = inverseLerp( e.clientY, rect.bottom, rect.top );
setter( clamp( x ), clamp( y ) );
};
const onMouseUp = () => {
this._callOnFinishChange();
window.removeEventListener( 'mousemove', onMouseMove );
window.removeEventListener( 'mouseup', onMouseUp );
};
};
makeDraggable( this.$knob1, ( x, y ) => {
const c = this.getValue();
c[ 0 ] = x;
c[ 1 ] = y;
this._callOnChange();
this.updateDisplay();
} );
makeDraggable( this.$knob2, ( x, y ) => {
const c = this.getValue();
c[ 2 ] = x;
c[ 3 ] = y;
this._callOnChange();
this.updateDisplay();
} );
this._initialValue = this.save();
this.updateDisplay();
}
updateDisplay() {
const c = this.getValue();
this.$knob1.setAttribute( 'cx', c[ 0 ] );
this.$knob1.setAttribute( 'cy', c[ 1 ] );
this.$knob2.setAttribute( 'cx', c[ 2 ] );
this.$knob2.setAttribute( 'cy', c[ 3 ] );
this.$line1.setAttribute( 'x2', c[ 0 ] );
this.$line1.setAttribute( 'y2', c[ 1 ] );
this.$line2.setAttribute( 'x2', c[ 2 ] );
this.$line2.setAttribute( 'y2', c[ 3 ] );
this.$path.setAttribute( 'd', `M 0 0 C ${c[ 0 ]} ${c[ 1 ]}, ${c[ 2 ]} ${c[ 3 ]}, 1 1` );
}
save() {
return Array.from( this.getValue() );
}
load( saved ) {
const arr = this.getValue();
arr[ 0 ] = saved[ 0 ];
arr[ 1 ] = saved[ 1 ];
arr[ 2 ] = saved[ 2 ];
arr[ 3 ] = saved[ 3 ];
this._callOnChange();
this._callOnFinishChange();
this.updateDisplay();
return this;
}
reset() {
return this.load( this._initialValue );
}
}
injectStyles( `
.lil-gui .controller.bezier svg {
width: 100%;
/* flip coordinates so that up is positive */
transform: scaleY(-1);
background-color: var(--widget-color);
border-radius: var(--widget-border-radius);
}
.lil-gui .controller.bezier svg * {
/* let us use a tiny viewbox without huge strokes */
vector-effect: non-scaling-stroke;
}
.lil-gui .controller.bezier path {
stroke: var(--number-color);
stroke-width: 2px;
fill: none;
}
.lil-gui .controller.bezier line {
stroke: var(--focus-color);
stroke-width: 1px;
}
.lil-gui .controller.bezier circle {
fill: var(--text-color);
cursor: pointer;
}` );
GUI.prototype.addBezier = function() {
return new BezierController( this, ...arguments );
};