-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Focal point + crop for featured images #20321
Comments
I had need of this feature and did a quick and dirty plugin that filters the First, I registered the /**
* Register post meta for featured image focal point
*/
function featured_image_focal_point_post_meta() {
register_post_meta( '', 'featured_image_focal_point', array(
'type' => 'object',
'description' => 'Focal point of the featured image',
'single' => true,
'show_in_rest' => array(
'schema' => array(
'type' => 'object',
'properties' => array(
'x' => array(
'type' => 'number',
),
'y' => array(
'type' => 'number',
),
),
),
),
) );
}
add_action( 'init', 'featured_image_focal_point_post_meta' ); Then I used /**
* EDITOR: Featured Image with Focal Point
*/
const { FocalPointPicker } = wp.components;
const { compose } = wp.compose;
const { withDispatch, withSelect } = wp.data;
const { Fragment } = wp.element;
const { addFilter } = wp.hooks;
const { __ } = wp.i18n;
function wrapPostFeaturedImage( PostFeaturedImage ) {
return compose(
applyWithSelect,
applyWithDispatch,
)( ( props ) => {
const {
media,
featuredImageFocalPoint,
setFeaturedImageFocalPoint,
} = props;
if ( media && media.source_url ) {
const url = media.source_url;
const { width, height } = media;
return (
<Fragment>
<PostFeaturedImage { ...props } />
<FocalPointPicker
label={ __( 'Focal point picker' ) }
url={ url }
dimensions={ { width, height } }
value={ featuredImageFocalPoint }
onChange={ ( newFocalPoint ) =>
setFeaturedImageFocalPoint( newFocalPoint )
}
/>
</Fragment>
);
}
return (
<PostFeaturedImage { ...props } />
);
} );
}
const applyWithSelect = withSelect( ( select ) => {
const { getEditedPostAttribute } = select( 'core/editor' );
const featuredImageFocalPoint = getEditedPostAttribute( 'meta' )[ 'featured_image_focal_point' ];
return {
featuredImageFocalPoint,
};
} );
const applyWithDispatch = withDispatch( ( dispatch ) => {
const { editPost } = dispatch( 'core/editor' );
return {
setFeaturedImageFocalPoint( focalPoint ) {
editPost( { meta: { featured_image_focal_point: focalPoint } } );
},
};
} );
addFilter(
'editor.PostFeaturedImage',
'centralex/wrap-post-featured-image',
wrapPostFeaturedImage
); EDIT: Using If this were to be a built-in feature, the theme would need to know how to use the focal point meta values, and it would perhaps be misleading to show the focal point picker by default if the theme doesn't reflect the setting. Perhaps this feature could be behind an For me personally, in the future I'd rather just use the existing Cover Image block, but it would need to be aware of the post's featured image, and Gutenberg's template features aren't quite to the point where I feel I can utilize them yet. Also, the Cover Image block uses $image = get_post_thumbnail_id();
$focal_point = get_post_meta( get_the_ID(), 'featured_image_focal_point', true );
$focal_point_style = ( $focal_point && $focal_point['x'] && $focal_point['y'] ) ? sprintf('object-position: %d%% %d%%', $focal_point['x']*100, $focal_point['y']*100) : '';
echo wp_get_attachment_image( $image, 'banner-xl', false, array( 'style' => $focal_point_style ) ); |
Fun fact for anyone attempting to use the code I posted above with a custom post type: your custom post type MUST have 'custom-fields' in its See #17018 (comment) Not sure how to modify the code to not run on unsupported post types... |
I'd like to know that as well. |
did this a little (but ultimately not so) differently with HigherOrderComponents and it seems to work just as well. const { __ } = wp.i18n;
const { addFilter } = wp.hooks;
const { Fragment } = wp.element;
const { createHigherOrderComponent } = wp.compose;
const { FocalPointPicker } = wp.components;
const { useEntityProp } = wp.coreData;
/**
* Add Focal Point Picker to Featured Image on posts.
*
* @param {function} PostFeaturedImage Featured Image component.
*
* @return {function} PostFeaturedImage Modified Featured Image component.
*/
const wrapPostFeaturedImage = createHigherOrderComponent(
(PostFeaturedImage) => {
return (props) => {
const { media } = props;
const [meta, setMeta] = useEntityProp('postType', 'post', 'meta');
const setFeaturedImageMeta = (val) => {
setMeta(
Object.assign({}, meta, {
featured_image_focal_point: val,
})
);
};
if (media && media.source_url) {
const url = media.source_url;
return (
<Fragment>
<PostFeaturedImage {...props} />
<FocalPointPicker
label={__('Focal point picker')}
url={url}
value={meta.featured_image_focal_point}
onChange={(newFocalPoint) => setFeaturedImageMeta(newFocalPoint)}
/>
</Fragment>
);
}
return <PostFeaturedImage {...props} />;
};
},
'wrapPostFeaturedImage'
);
addFilter(
'editor.PostFeaturedImage',
'abc/featured-image-control',
wrapPostFeaturedImage
); couple things I noted:
Highly recommend making the |
"0" is a valid value for x and y, but returns false in the statement above, btw, which would prevent any style from being added to the |
@cr0ybot or @ryanapsmith I'm interested in getting some of your code working, to see if I can push this along at all. Edit: I've done some research into how this code might run in a Gutenberg context, and it's a little more clear now, but if you happen to have a plugin where this code is in context, it would still be very helpful! |
@NickGreen you got it right, you have to enqueue the JS file that contains the snippet I provided. /**
* Register the JavaScript for the admin area.
*
* @since 1.0.0
*/
public function my_enqueue_scripts() {
wp_enqueue_script( 'admin-blocks', 'admin-blocks.js', array(
'jquery',
'wp-dom-ready',
'wp-i18n',
'wp-hooks',
'wp-element',
'wp-block-editor',
'wp-compose',
'wp-components',
'wp-core-data',
'wp-plugins',
'wp-edit-post',
), '1.0.0', false );
}
add_action('admin_enqueue_scripts', 'my_enqueue_scripts');
Insert this php into your theme's Next time you load the editor, the focal point picker should show up in the sidebar for any post type with featured image support. |
This is a really interesting topic, and something that deserves thought into the best way that it would actually work in practice. First of all, the cover image focal point is a set of x and y coordinates that determine the offset of the image when loaded into a container. It does not re-crop the image. If this same approach were used for the featured image, then you're relying on the theme developer to output the featured image in the various locations using those coordinates. This technique also isn't as performant as cropping would be; you're loading a larger image into a smaller container, and moving it around. This kind of defeats the whole purpose of having thumbnails of various sizes which are used in the appropriate context (Consider an archive page which could have tens or hundreds of featured image thumbnails. Would you want to load the full sized image in all of those cases?). Consider how this 3rd party plugin does it: https://wecodepixels.com/shop/theia-smart-thumbnails-for-wordpress/ This is how, as a user, I would assume a focal point picker would work for a featured image. Similar to cropping an image from the Gutenberg image block, I would assume that setting a focal point would create a new version of the image, and re-crop all of the thumbnails based on the new focal point. This would not require theme developers to do anything to support it, since displaying various image sizes in various contexts is already standard practice. |
@NickGreen I'd personally prefer a lossless approach. Selecting a focal point allows the theme developer greater control over aspect ratios, with the content editor only having to worry about placement within an image container, while the developer dictates final sizing across different breakpoints with the container. Cropping would produce undesirable effects there (like trying to cram a square crop into a hero region with a 16:9 aspect ratio). The focal point picker suffices here – for image manipulation like cropping, that's more appropriate in the body of a post than a region predefined in a theme's template. |
@ryanapsmith: Good point - but wouldn't |
no this is just cropping, that is not preferable., we need focal point for responsive images that use object-fill.. so cropping is out of the question |
@cr0ybot |
had to rewrite it like this... probably am doing something wrong... const {__} = wp.i18n;
const {addFilter} = wp.hooks;
const {Fragment} = wp.element;
const {createHigherOrderComponent} = wp.compose;
const {FocalPointPicker} = wp.components;
const useEntityProp = wp.coreData.useEntityProp;
const el = wp.element.createElement;
/**
* Add Focal Point Picker to Featured Image on posts.
*
* @param {function} PostFeaturedImage Featured Image component.
*
* @return {function} PostFeaturedImage Modified Featured Image component.
*/
const wrapPostFeaturedImage = createHigherOrderComponent(
(PostFeaturedImage) => {
return (props) => {
const {media} = props;
const [meta, setMeta] = useEntityProp('postType', 'project', 'meta');
const setFeaturedImageMeta = (val) => {
if (meta)
setMeta(
Object.assign({}, meta, {
featured_image_focal_point: val,
})
);
else {
setMeta({
featured_image_focal_point: val,
});
}
};
if (media && media.source_url) {
const url = media.source_url;
return el(
wp.element.Fragment,
{},
'Prepend above',
el(
PostFeaturedImage,
props
),
el(
wp.components.FocalPointPicker,
{
label: __('Focal point picker'),
value: meta?.featured_image_focal_point,
url: url,
onChange: (newFocalPoint) => setFeaturedImageMeta(newFocalPoint)
}
)
)
}
return el(
PostFeaturedImage,
props
)
};
},
'wrapPostFeaturedImage'
);
wp.hooks.addFilter(
'editor.PostFeaturedImage',
'koraysels/wrap-post-featured-image',
wrapPostFeaturedImage
); |
If you want it to be displayed in wp-graphql i wrote this: add_action('graphql_register_types', function () {
register_graphql_object_type('FocalPoint', [
'description' => __("FocalPoint of image", 'your-textdomain'),
'fields' => [
'x' => [
'type' => 'Number',
'description' => __('x focal point', 'your-textdomain'),
],
'y' => [
'type' => 'Number',
'description' => __('y focal point', 'your-textdomain'),
]
],
]);
});
add_action('graphql_register_types', function () {
register_graphql_field('NodeWithFeaturedImage', 'featuredImageFocalPoint', [
'type' => 'FocalPoint',
'description' => __('Focal Point of the featured Image', 'your-textdomain'),
'resolve' => function (\WPGraphQL\Model\Post $post, $args, $context, $info) {
return get_post_meta($post->ID, 'featured_image_focal_point', true);
}
]);
}); |
You would need to be set up with a build step to use JSX syntax. Also, sorry for being absent from this thread but @ryanapsmith did a great job condensing/simplifying the implementation via Maybe I'll revisit this next time I have a need for it, though with the new Post Featured Image block (#19875) maybe this is not quite as relevant anymore, especially with the push towards full site editing instead of coded themes. |
This is such a good idea. Setting the focal point would ideally be capable on all images in the Media Library. Most themes register a variety of different sizes, with different aspect ratios, and many plugins register their own as well. Displaying these images with a reasonable crop would be such a benefit to sites. There have been various plugins attempting this over the years, but most seem abandoned or add way too much additional complexity beyond simply setting a focal point. |
FWIW I like the way https://wordpress.org/plugins/better-image-sizes/ implements this (h/t @kubiqsk). The admin UI is really nice, providing previews of how the cropped image will look at different aspect ratios. It also includes face detection, which in my experience is the primary reason for wanting to set the focal point in the first place. |
Is your feature request related to a problem? Please describe.
It would be nice if the Featured Image field in Gutenberg editor also supports focal point + crop like in e.g. the cover block.
Describe the solution you'd like
Allow/offer focal point + crop controls for featured image fields.
Describe alternatives you've considered
Using a plugin for this, but this will introduce completely different, "classic" UI.
Edit: Similar plugin that does it for all media though:
https://wordpress.org/plugins/wp-smartcrop/
The text was updated successfully, but these errors were encountered: