Skip to content

Commit

Permalink
calculate media average color only once
Browse files Browse the repository at this point in the history
  • Loading branch information
draganescu committed Sep 1, 2023
1 parent 848433f commit 3787118
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 76 deletions.
58 changes: 24 additions & 34 deletions packages/block-library/src/cover/edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import namesPlugin from 'colord/plugins/names';
* WordPress dependencies
*/
import { useEntityProp, store as coreStore } from '@wordpress/core-data';
import { useEffect, useMemo, useRef } from '@wordpress/element';
import { useMemo, useRef, useState } from '@wordpress/element';
import { Placeholder, Spinner } from '@wordpress/components';
import { compose, useResizeObserver } from '@wordpress/compose';
import {
Expand Down Expand Up @@ -38,6 +38,7 @@ import {
getPositionClassName,
mediaPosition,
getCoverIsDark,
getAverageMediaColor,
} from '../shared';
import CoverInspectorControls from './inspector-controls';
import CoverBlockControls from './block-controls';
Expand Down Expand Up @@ -97,6 +98,8 @@ function CoverEdit( {
tagName: TagName = 'div',
} = attributes;

const [ averageMediaColor, setAverageMediaColor ] = useState( undefined );

const [ featuredImage ] = useEntityProp(
'postType',
postType,
Expand All @@ -114,26 +117,6 @@ function CoverEdit( {
);
const mediaUrl = media?.source_url;

useEffect( () => {
async function setIsDark() {
__unstableMarkNextChangeAsNotPersistent();
const isDarkSetting = await getCoverIsDark(
mediaUrl,
dimRatio,
overlayColor.color
);
setAttributes( {
isDark: isDarkSetting,
} );
}
if ( useFeaturedImage ) {
setIsDark();
}
// We only ever want to run this effect if the mediaUrl changes.
// All other changes to the isDark state are handled in the appropriate event handlers.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ mediaUrl ] );

// instead of destructuring the attributes
// we define the url and background type
// depending on the value of the useFeaturedImage flag
Expand All @@ -151,15 +134,16 @@ function CoverEdit( {
const setMedia = attributesFromMedia( setAttributes, dimRatio );

const onSelectMedia = async ( newMedia ) => {
await setMedia( newMedia );
// Only pass the url to getCoverIsDark if the media is an image as video is not handled.
const newUrl = newMedia?.type === 'image' ? newMedia.url : undefined;
const averageBackgroundColor = await getAverageMediaColor( newUrl );
setAverageMediaColor( averageBackgroundColor );
await setMedia( newMedia, averageBackgroundColor );
};

const onClearMedia = async () => {
const isDarkSetting = await getCoverIsDark(
undefined,
dimRatio,
overlayColor.color
);
setAverageMediaColor( undefined );
const isDarkSetting = getCoverIsDark( dimRatio, overlayColor.color );
setAttributes( {
url: undefined,
id: undefined,
Expand All @@ -173,7 +157,11 @@ function CoverEdit( {
};

const onSetOverlayColor = async ( colorValue ) => {
const isDarkSetting = await getCoverIsDark( url, dimRatio, colorValue );
const isDarkSetting = getCoverIsDark(
dimRatio,
colorValue,
averageMediaColor
);
setOverlayColor( colorValue );
__unstableMarkNextChangeAsNotPersistent();
setAttributes( {
Expand All @@ -182,10 +170,10 @@ function CoverEdit( {
};

const onUpdateDimRatio = async ( newDimRatio ) => {
const isDarkSetting = await getCoverIsDark(
url,
const isDarkSetting = getCoverIsDark(
newDimRatio,
overlayColor.color
overlayColor.color,
averageMediaColor
);

setAttributes( {
Expand Down Expand Up @@ -278,9 +266,11 @@ function CoverEdit( {
};

const toggleUseFeaturedImage = async () => {
const isDarkSetting = await ( useFeaturedImage
? getCoverIsDark( undefined, dimRatio, overlayColor.color )
: getCoverIsDark( mediaUrl, dimRatio, overlayColor.color ) );
const isDarkSetting = getCoverIsDark(
dimRatio,
overlayColor.color,
averageMediaColor
);
setAttributes( {
id: undefined,
url: undefined,
Expand Down
68 changes: 26 additions & 42 deletions packages/block-library/src/cover/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function dimRatioToClass( ratio ) {
}

export function attributesFromMedia( setAttributes, dimRatio ) {
return async ( media ) => {
return ( media, averageBackgroundColor ) => {
if ( ! media || ! media.url ) {
setAttributes( { url: undefined, id: undefined, isDark } );
return;
Expand Down Expand Up @@ -73,15 +73,7 @@ export function attributesFromMedia( setAttributes, dimRatio ) {
mediaType = media.type;
}

// Only pass the url to getCoverIsDark if the media is an image as video is not handled.
const newUrl = media?.type === 'image' ? media.url : undefined;
const customOverlayColor = await getAverageMediaColor( newUrl );

const isDark = await getCoverIsDark(
newUrl,
dimRatio,
customOverlayColor
);
const isDark = getCoverIsDark( dimRatio, averageBackgroundColor?.hex );

setAttributes( {
isDark,
Expand All @@ -90,7 +82,7 @@ export function attributesFromMedia( setAttributes, dimRatio ) {
id: media.id,
alt: media?.alt,
overlayColor: undefined,
customOverlayColor,
customOverlayColor: averageBackgroundColor?.hex,
backgroundType: mediaType,
focalPoint: undefined,
...( mediaType === VIDEO_BACKGROUND_TYPE
Expand Down Expand Up @@ -168,42 +160,28 @@ function retrieveFastAverageColor() {
* See the comments below for more details about which aspects take priority when
* calculating the relative darkness of the Cover.
*
* @param {string} url
* @param {number} dimRatio
* @param {string} overlayColor
* @param {Object} averageBackgroundColor
* @return {Promise<boolean>} True if cover should be considered to be dark.
*/
export async function getCoverIsDark( url, dimRatio = 50, overlayColor ) {
export function getCoverIsDark(
dimRatio = 50,
overlayColor,
averageBackgroundColor = undefined
) {
const overlay = colord( overlayColor )
.alpha( dimRatio / 100 )
.toRgb();

if ( url ) {
try {
const imgCrossOrigin = applyFilters(
'media.crossOrigin',
undefined,
url
);
const {
value: [ r, g, b, a ],
} = await retrieveFastAverageColor().getColorAsync( url, {
// Previously the default color was white, but that changed
// in v6.0.0 so it has to be manually set now.
defaultColor: [ 255, 255, 255, 255 ],
// Errors that come up don't reject the promise, so error
// logging has to be silenced with this option.
silent: process.env.NODE_ENV === 'production',
crossOrigin: imgCrossOrigin,
} );
// FAC uses 0-255 for alpha, but colord expects 0-1.
const media = { r, g, b, a: a / 255 };
const composite = compositeSourceOver( overlay, media );
return colord( composite ).isDark();
} catch ( error ) {
// If there's an error, just assume the image is dark.
return true;
}
if ( averageBackgroundColor ) {
const {
value: [ r, g, b, a ],
} = averageBackgroundColor;
// FAC uses 0-255 for alpha, but colord expects 0-1.
const media = { r, g, b, a: a / 255 };
const composite = compositeSourceOver( overlay, media );
return colord( composite ).isDark();
}

// Assume a white background because it isn't easy to get the actual
Expand All @@ -213,6 +191,12 @@ export async function getCoverIsDark( url, dimRatio = 50, overlayColor ) {
return colord( composite ).isDark();
}

/**
* This method evaluates the average color of a background image
*
* @param {string} url
* @return {object|null} Null the color can't be computed, or the color.
*/
export async function getAverageMediaColor( url ) {
if ( url ) {
try {
Expand All @@ -231,11 +215,11 @@ export async function getAverageMediaColor( url ) {
crossOrigin: imgCrossOrigin,
} );
// FAC uses 0-255 for alpha, but colord expects 0-1.
return color.hex;
return color;
} catch ( error ) {
// If there's an error, just assume the image is dark.
return undefined;
return null;
}
}
return undefined;
return null;
}

0 comments on commit 3787118

Please sign in to comment.