Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try SVG-based duotone filter #88

Merged
merged 9 commits into from
Sep 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -31,6 +32,7 @@ import * as wavesBlock from '../blocks/waves/src';

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