Skip to content

Commit

Permalink
Try SVG-based duotone filter (#88)
Browse files Browse the repository at this point in the history
Co-authored-by: jasmussen <joen@automattic.com>
  • Loading branch information
ajlende and jasmussen committed Sep 1, 2020
1 parent 04d53e3 commit 778e3c4
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 0 deletions.
60 changes: 60 additions & 0 deletions blocks/duotone-filter/duotone.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

if ( ! function_exists( 'hex2rgb' ) ) {
function hex2rgb( $color ) {
if ( strlen( $color ) === 4 ) {
$r = hexdec( substr( $color, 1, 1 ) . substr( $color, 1, 1 ) );
$g = hexdec( substr( $color, 2, 1 ) . substr( $color, 2, 1 ) );
$b = hexdec( substr( $color, 3, 1 ) . substr( $color, 3, 1 ) );
} elseif ( strlen( $color ) === 7 ) {
$r = hexdec( substr( $color, 1, 2 ) );
$g = hexdec( substr( $color, 3, 2 ) );
$b = hexdec( substr( $color, 5, 2 ) );
} else {
return array();
}

return array(
'r' => $r / 0xff,
'g' => $g / 0xff,
'b' => $b / 0xff,
);
}
}

// phpcs:disable
$duotone_id = $block['attrs']['duotoneId'];
$duotone_dark = hex2rgb( $block['attrs']['duotoneDark'] );
$duotone_light = hex2rgb( $block['attrs']['duotoneLight'] );
// phpcs:enable

?>

<svg
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 0 0"
width="0"
height="0"
focusable="false"
role="none"
style="visibility: hidden !important; position: absolute !important; left: -9999px !important; overflow: hidden !important;"
>
<defs>
<filter id="<?php echo $duotone_id; ?>">
<feColorMatrix
type="matrix"
<?php // phpcs:disable ?>
values=".2989 .5870 .1140 0 0
.2989 .5870 .1140 0 0
.2989 .5870 .1140 0 0
0 0 0 1 0"
<?php // phpcs:enable ?>
/>
<feComponentTransfer color-interpolation-filters="sRGB">
<feFuncR type="table" tableValues="<?php echo $duotone_dark['r']; ?> <?php echo $duotone_light['r']; ?>" />
<feFuncG type="table" tableValues="<?php echo $duotone_dark['g']; ?> <?php echo $duotone_light['g']; ?>" />
<feFuncB type="table" tableValues="<?php echo $duotone_dark['b']; ?> <?php echo $duotone_light['b']; ?>" />
</feComponentTransfer>
</filter>
</defs>
</svg>
31 changes: 31 additions & 0 deletions blocks/duotone-filter/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

add_action( 'init', function() {
register_block_type( 'a8c/duotone-filter', [
'editor_script' => 'block-experiments',
'style' => 'block-experiments',
'editor_style' => 'block-experiments-editor',
] );

function is_supported_block( $block ) {
$supported_blocks = [ 'core/image', 'core/cover' ];
return in_array( $block['blockName'], $supported_blocks, true );
}

add_filter( 'render_block', function ( $block_content, $block ) {
if (
! is_supported_block( $block ) ||
! array_key_exists( 'duotoneId', $block['attrs'] ) ||
! array_key_exists( 'duotoneDark', $block['attrs'] ) ||
! array_key_exists( 'duotoneLight', $block['attrs'] )
) {
return $block_content;
}

ob_start();
include 'duotone.php';
$duotone = ob_get_clean();

return $duotone . $block_content;
}, 10, 2 );
} );
79 changes: 79 additions & 0 deletions blocks/duotone-filter/src/duotone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* WordPress dependencies
*/
import { SVG } from '@wordpress/components';

const hex2rgb = ( color = '' ) => {
let r = '0';
let g = '0';
let b = '0';

if ( color.length === 4 ) {
r = '0x' + color[ 1 ] + color[ 1 ];
g = '0x' + color[ 2 ] + color[ 2 ];
b = '0x' + color[ 3 ] + color[ 3 ];
} else if ( color.length === 7 ) {
r = '0x' + color[ 1 ] + color[ 2 ];
g = '0x' + color[ 3 ] + color[ 4 ];
b = '0x' + color[ 5 ] + color[ 6 ];
} else {
return {};
}

return {
r: r / 0xff,
g: g / 0xff,
b: b / 0xff,
};
};

function Duotone( { id: duotoneId, darkColor, lightColor } ) {
const duotoneDark = hex2rgb( darkColor );
const duotoneLight = hex2rgb( lightColor );

return (
<SVG
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="0 0 0 0"
width="0"
height="0"
focusable="false"
role="none"
style={ {
visibility: 'hidden',
position: 'absolute',
left: '-9999px',
overflow: 'hidden',
} }
>
<defs>
<filter id={ duotoneId }>
<feColorMatrix
type="matrix"
// prettier-ignore
values=".2989 .5870 .1140 0 0
.2989 .5870 .1140 0 0
.2989 .5870 .1140 0 0
0 0 0 1 0"
/>
<feComponentTransfer colorInterpolationFilters="sRGB">
<feFuncR
type="table"
tableValues={ `${ duotoneDark.r } ${ duotoneLight.r }` }
/>
<feFuncG
type="table"
tableValues={ `${ duotoneDark.g } ${ duotoneLight.g }` }
/>
<feFuncB
type="table"
tableValues={ `${ duotoneDark.b } ${ duotoneLight.b }` }
/>
</feComponentTransfer>
</filter>
</defs>
</SVG>
);
}

export default Duotone;
132 changes: 132 additions & 0 deletions blocks/duotone-filter/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/**
* WordPress dependencies
*/
import { InspectorControls, PanelColorSettings } from '@wordpress/block-editor';
import { createHigherOrderComponent, useInstanceId } from '@wordpress/compose';
import { useEffect } from '@wordpress/element';
import { addFilter } from '@wordpress/hooks';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import Duotone from './duotone';

const SUPPORTED_BLOCKS = [ 'core/image', 'core/cover' ];

export const isSupportedBlock = ( blockName ) =>
SUPPORTED_BLOCKS.includes( blockName );

const withDuotoneAttributes = ( settings, blockName ) => {
if ( isSupportedBlock( blockName ) ) {
Object.assign( settings.attributes, {
duotoneId: {
type: 'string',
},
duotoneDark: {
type: 'string',
},
duotoneLight: {
type: 'string',
},
} );
}
return settings;
};

const withDuotoneEditorControls = createHigherOrderComponent(
( BlockEdit ) => ( props ) => {
const { name: blockName, attributes, setAttributes } = props;

if ( ! isSupportedBlock( blockName ) ) {
return <BlockEdit { ...props } />;
}

const instanceId = useInstanceId( BlockEdit );

useEffect( () => {
setAttributes( {
duotoneId: `a8c-duotone-filter-${ instanceId }`,
} );
}, [ instanceId ] );

return (
<>
<InspectorControls>
<PanelColorSettings
title={ __( 'Duotone', 'block-experiments' ) }
initialOpen
colorSettings={ [
{
label: __( 'Dark Color', 'block-experiments' ),
value: attributes.duotoneDark,
onChange: ( duotoneDark ) =>
setAttributes( { duotoneDark } ),
},
{
label: __( 'Light Color', 'block-experiments' ),
value: attributes.duotoneLight,
onChange: ( duotoneLight ) =>
setAttributes( { duotoneLight } ),
},
] }
/>
</InspectorControls>
{ attributes.duotoneDark &&
attributes.duotoneLight &&
attributes.duotoneId ? (
<>
<Duotone
id={ attributes.duotoneId }
darkColor={ attributes.duotoneDark }
lightColor={ attributes.duotoneLight }
/>
<div
style={ {
filter: `url( #${ attributes.duotoneId } )`,
} }
>
<BlockEdit { ...props } />
</div>
</>
) : (
<BlockEdit { ...props } />
) }
</>
);
},
'withDuotoneEditorControls'
);

function addDuotoneFilterStyle( props, block, attributes ) {
if (
! isSupportedBlock( block.name ) ||
! attributes.duotoneDark ||
! attributes.duotoneLight ||
! attributes.duotoneId
) {
return props;
}

const { style = {} } = props;

return { style: { ...style, filter: `url( #${ attributes.duotoneId } )` } };
}

export function registerBlock() {
addFilter(
'editor.BlockEdit',
'a8c/duotone-filter/with-editor-controls',
withDuotoneEditorControls
);
addFilter(
'blocks.registerBlockType',
'a8c/duotone-filter/add-attributes',
withDuotoneAttributes
);
addFilter(
'blocks.getSaveContent.extraProps',
'a8c/duotone-filter/add-filter-style',
addDuotoneFilterStyle
);
}
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ setCategories( [
* Load all our blocks
*/
import * as bauhausCentenaryBlock from '../blocks/bauhaus-centenary/src';
import * as duotoneFilter from '../blocks/duotone-filter/src';
import * as eventBlock from '../blocks/event/src';
import * as layoutGridBlock from '../blocks/layout-grid/src';
import * as motionBackgroundBlock from '../blocks/motion-background/src';
Expand All @@ -32,6 +33,7 @@ import * as bookBlock from '../blocks/book/src';

// Instantiate the blocks, adding them to our block category
bauhausCentenaryBlock.registerBlock();
duotoneFilter.registerBlock();
eventBlock.registerBlock();
layoutGridBlock.registerBlock();
motionBackgroundBlock.registerBlock();
Expand Down

0 comments on commit 778e3c4

Please sign in to comment.