|
@@ -8,7 +8,8 @@
|
|
|
const geometry = new THREE.PlaneGeometry( texture.image.width * 0.001, texture.image.height * 0.001 );
|
|
|
const material = new THREE.MeshBasicMaterial( {
|
|
|
map: texture,
|
|
|
- toneMapped: false
|
|
|
+ toneMapped: false,
|
|
|
+ transparent: true
|
|
|
} );
|
|
|
super( geometry, material );
|
|
|
|
|
@@ -110,6 +111,7 @@
|
|
|
function html2canvas( element ) {
|
|
|
|
|
|
const range = document.createRange();
|
|
|
+ const color = new THREE.Color();
|
|
|
|
|
|
function Clipper( context ) {
|
|
|
|
|
@@ -176,15 +178,29 @@
|
|
|
|
|
|
}
|
|
|
|
|
|
- context.font = style.fontSize + ' ' + style.fontFamily;
|
|
|
+ context.font = style.fontWeight + ' ' + style.fontSize + ' ' + style.fontFamily;
|
|
|
context.textBaseline = 'top';
|
|
|
context.fillStyle = style.color;
|
|
|
- context.fillText( string, x, y );
|
|
|
+ context.fillText( string, x, y + parseFloat( style.fontSize ) * 0.1 );
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
+ function buildRectPath( x, y, w, h, r ) {
|
|
|
+
|
|
|
+ if ( w < 2 * r ) r = w / 2;
|
|
|
+ if ( h < 2 * r ) r = h / 2;
|
|
|
+ context.beginPath();
|
|
|
+ context.moveTo( x + r, y );
|
|
|
+ context.arcTo( x + w, y, x + w, y + h, r );
|
|
|
+ context.arcTo( x + w, y + h, x, y + h, r );
|
|
|
+ context.arcTo( x, y + h, x, y, r );
|
|
|
+ context.arcTo( x, y, x + w, y, r );
|
|
|
+ context.closePath();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
function drawBorder( style, which, x, y, width, height ) {
|
|
|
|
|
|
const borderWidth = style[ which + 'Width' ];
|
|
@@ -194,6 +210,7 @@
|
|
|
if ( borderWidth !== '0px' && borderStyle !== 'none' && borderColor !== 'transparent' && borderColor !== 'rgba(0, 0, 0, 0)' ) {
|
|
|
|
|
|
context.strokeStyle = borderColor;
|
|
|
+ context.lineWidth = parseFloat( borderWidth );
|
|
|
context.beginPath();
|
|
|
context.moveTo( x, y );
|
|
|
context.lineTo( x + width, y + height );
|
|
@@ -243,31 +260,146 @@
|
|
|
y = rect.top - offset.top - 0.5;
|
|
|
width = rect.width;
|
|
|
height = rect.height;
|
|
|
- style = window.getComputedStyle( element );
|
|
|
+ style = window.getComputedStyle( element ); // Get the border of the element used for fill and border
|
|
|
+
|
|
|
+ buildRectPath( x, y, width, height, parseFloat( style.borderRadius ) );
|
|
|
const backgroundColor = style.backgroundColor;
|
|
|
|
|
|
if ( backgroundColor !== 'transparent' && backgroundColor !== 'rgba(0, 0, 0, 0)' ) {
|
|
|
|
|
|
context.fillStyle = backgroundColor;
|
|
|
- context.fillRect( x, y, width, height );
|
|
|
+ context.fill();
|
|
|
+
|
|
|
+ } // If all the borders match then stroke the round rectangle
|
|
|
+
|
|
|
+
|
|
|
+ const borders = [ 'borderTop', 'borderLeft', 'borderBottom', 'borderRight' ];
|
|
|
+ let match = true;
|
|
|
+ let prevBorder = null;
|
|
|
+
|
|
|
+ for ( const border of borders ) {
|
|
|
+
|
|
|
+ if ( prevBorder !== null ) {
|
|
|
+
|
|
|
+ match = style[ border + 'Width' ] === style[ prevBorder + 'Width' ] && style[ border + 'Color' ] === style[ prevBorder + 'Color' ] && style[ border + 'Style' ] === style[ prevBorder + 'Style' ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( match === false ) break;
|
|
|
+ prevBorder = border;
|
|
|
|
|
|
}
|
|
|
|
|
|
- drawBorder( style, 'borderTop', x, y, width, 0 );
|
|
|
- drawBorder( style, 'borderLeft', x, y, 0, height );
|
|
|
- drawBorder( style, 'borderBottom', x, y + height, width, 0 );
|
|
|
- drawBorder( style, 'borderRight', x + width, y, 0, height );
|
|
|
+ if ( match === true ) {
|
|
|
+
|
|
|
+ // They all match so stroke the rectangle from before allows for border-radius
|
|
|
+ const width = parseFloat( style.borderTopWidth );
|
|
|
+
|
|
|
+ if ( style.borderTopWidth !== '0px' && style.borderTopStyle !== 'none' && style.borderTopColor !== 'transparent' && style.borderTopColor !== 'rgba(0, 0, 0, 0)' ) {
|
|
|
|
|
|
- if ( element.type === 'color' || element.type === 'text' || element.type === 'number' ) {
|
|
|
+ context.strokeStyle = style.borderTopColor;
|
|
|
+ context.lineWidth = width;
|
|
|
+ context.stroke();
|
|
|
|
|
|
- clipper.add( {
|
|
|
- x: x,
|
|
|
- y: y,
|
|
|
- width: width,
|
|
|
- height: height
|
|
|
- } );
|
|
|
- drawText( style, x + parseInt( style.paddingLeft ), y + parseInt( style.paddingTop ), element.value );
|
|
|
- clipper.remove();
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ // Otherwise draw individual borders
|
|
|
+ drawBorder( style, 'borderTop', x, y, width, 0 );
|
|
|
+ drawBorder( style, 'borderLeft', x, y, 0, height );
|
|
|
+ drawBorder( style, 'borderBottom', x, y + height, width, 0 );
|
|
|
+ drawBorder( style, 'borderRight', x + width, y, 0, height );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( element instanceof HTMLInputElement ) {
|
|
|
+
|
|
|
+ let accentColor = style.accentColor;
|
|
|
+ if ( accentColor === undefined || accentColor === 'auto' ) accentColor = style.color;
|
|
|
+ color.set( accentColor );
|
|
|
+ const luminance = Math.sqrt( 0.299 * color.r ** 2 + 0.587 * color.g ** 2 + 0.114 * color.b ** 2 );
|
|
|
+ const accentTextColor = luminance < 0.5 ? 'white' : '#111111';
|
|
|
+
|
|
|
+ if ( element.type === 'radio' ) {
|
|
|
+
|
|
|
+ buildRectPath( x, y, width, height, height );
|
|
|
+ context.fillStyle = 'white';
|
|
|
+ context.strokeStyle = accentColor;
|
|
|
+ context.lineWidth = 1;
|
|
|
+ context.fill();
|
|
|
+ context.stroke();
|
|
|
+
|
|
|
+ if ( element.checked ) {
|
|
|
+
|
|
|
+ buildRectPath( x + 2, y + 2, width - 4, height - 4, height );
|
|
|
+ context.fillStyle = accentColor;
|
|
|
+ context.strokeStyle = accentTextColor;
|
|
|
+ context.lineWidth = 2;
|
|
|
+ context.fill();
|
|
|
+ context.stroke();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( element.type === 'checkbox' ) {
|
|
|
+
|
|
|
+ buildRectPath( x, y, width, height, 2 );
|
|
|
+ context.fillStyle = element.checked ? accentColor : 'white';
|
|
|
+ context.strokeStyle = element.checked ? accentTextColor : accentColor;
|
|
|
+ context.lineWidth = 1;
|
|
|
+ context.stroke();
|
|
|
+ context.fill();
|
|
|
+
|
|
|
+ if ( element.checked ) {
|
|
|
+
|
|
|
+ const currentTextAlign = context.textAlign;
|
|
|
+ context.textAlign = 'center';
|
|
|
+ const properties = {
|
|
|
+ color: accentTextColor,
|
|
|
+ fontFamily: style.fontFamily,
|
|
|
+ fontSize: height + 'px',
|
|
|
+ fontWeight: 'bold'
|
|
|
+ };
|
|
|
+ drawText( properties, x + width / 2, y, '✔' );
|
|
|
+ context.textAlign = currentTextAlign;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( element.type === 'range' ) {
|
|
|
+
|
|
|
+ const [ min, max, value ] = [ 'min', 'max', 'value' ].map( property => parseFloat( element[ property ] ) );
|
|
|
+ const position = ( value - min ) / ( max - min ) * ( width - height );
|
|
|
+ buildRectPath( x, y + height / 4, width, height / 2, height / 4 );
|
|
|
+ context.fillStyle = accentTextColor;
|
|
|
+ context.strokeStyle = accentColor;
|
|
|
+ context.lineWidth = 1;
|
|
|
+ context.fill();
|
|
|
+ context.stroke();
|
|
|
+ buildRectPath( x, y + height / 4, position + height / 2, height / 2, height / 4 );
|
|
|
+ context.fillStyle = accentColor;
|
|
|
+ context.fill();
|
|
|
+ buildRectPath( x + position, y, height, height, height / 2 );
|
|
|
+ context.fillStyle = accentColor;
|
|
|
+ context.fill();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( element.type === 'color' || element.type === 'text' || element.type === 'number' ) {
|
|
|
+
|
|
|
+ clipper.add( {
|
|
|
+ x: x,
|
|
|
+ y: y,
|
|
|
+ width: width,
|
|
|
+ height: height
|
|
|
+ } );
|
|
|
+ drawText( style, x + parseInt( style.paddingLeft ), y + parseInt( style.paddingTop ), element.value );
|
|
|
+ clipper.remove();
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
@@ -345,6 +477,19 @@
|
|
|
|
|
|
element.dispatchEvent( new MouseEvent( event, mouseEventInit ) );
|
|
|
|
|
|
+ if ( element instanceof HTMLInputElement && element.type === 'range' && ( event === 'mousedown' || event === 'click' ) ) {
|
|
|
+
|
|
|
+ const [ min, max ] = [ 'min', 'max' ].map( property => parseFloat( element[ property ] ) );
|
|
|
+ const width = rect.width;
|
|
|
+ const offsetX = x - rect.x;
|
|
|
+ const proportion = offsetX / width;
|
|
|
+ element.value = min + ( max - min ) * proportion;
|
|
|
+ element.dispatchEvent( new InputEvent( 'input', {
|
|
|
+ bubbles: true
|
|
|
+ } ) );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
for ( let i = 0; i < element.childNodes.length; i ++ ) {
|