Yesterday I programmed a feature for my blog and would like to make it available to you as well. I write blog posts with a lot of text and many subheadings. So that does not get boring, I’ve been thinking about programming this feature. I also think that this is a nice subdivision of the sections. This is how it looks like:
The Code
I didn’t really do much on the code side. I’ve focused more on my css skills to accomplish that. The codes look like this:
php
<?php
defined( 'ABSPATH' ) || exit;
wp_enqueue_style(
basename(__DIR__).'-style',
get_template_directory_uri().'/blocks/'.basename(__DIR__).'/'.basename(__DIR__).'.css'
);
function register_full_image_title() {
if ( ! function_exists( 'register_block_type' ) ) {
// Gutenberg is not active.
return;
}
wp_enqueue_script(
basename(__DIR__),
get_template_directory_uri().'/blocks/'.basename(__DIR__).'/block.js',
array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor' )
);
register_block_type( 'konzeptcode/'.basename(__DIR__), array(
'editor_script' => basename(__DIR__),
'style' => basename(__DIR__).'-style',
'editor_style' => basename(__DIR__).'-style'
) );
}
add_action( 'enqueue_block_editor_assets', 'register_full_image_title' );
add_action( 'enqueue_block_assets', 'register_full_image_title' );
js (backend)
const { registerBlockType } = wp.blocks;
const { PanelBody, ToggleControl, TextControl, Button } = wp.components;
const { Fragment } = wp.element;
const { InspectorControls, MediaUpload } = wp.editor;
let { withState } = wp.compose;
registerBlockType( 'konzeptcode/full-image-title', {
title: 'Vollbild mit Titel',
icon: 'align-center',
category: 'layout',
keywords: ['fazit', 'headline', 'full'],
attributes: {
text: {
type: 'string',
default: '',
},
imageData: {
type: 'object',
default: {}
},
},
edit: ( props ) => {
const {
attributes: {
text,
imageData
},
setAttributes,
className
} = props;
return <Fragment>
<div
class="full-image-title-preview"
style={{ backgroundImage : (imageData.hasOwnProperty('fhd-url'))? "url("+imageData['fhd-url']+")" : "url("+imageData['full-url']+")" }}
>
<TextControl
label="Headline"
class="full-image-title-text"
value={text}
onChange={ ( text ) => setAttributes( { text } ) }
/>
<MediaUpload
onSelect={ function(media){
var image = {};
['uhd','wqhd','fhd'].forEach(function (size){
if (media.sizes.hasOwnProperty(size)) {
image[size+'-url'] = media.sizes[size].url;
image[size+'-width'] = media.sizes[size].width;
image[size+'-height'] = media.sizes[size].height;
}
});
if(media.sizes.full.width == 1920){
image['fhd-url'] = media.sizes.full.url;
image['fhd-width'] = media.sizes.full.width;
image['fhd-height'] = media.sizes.full.height;
}
if(Object.keys(image).length === 0){
image['full-url'] = media.sizes.full.url;
image['full-width'] = media.sizes.full.width;
image['full-height'] = media.sizes.full.height;
}
image['thumb-id'] = media.id;
if(Object.keys(image).length >= 4){
setAttributes( {
imageData: image
} );
}
} }
value={ (imageData.hasOwnProperty('thumb-id')? imageData.id : 0 ) }
render={ ( { open } ) => (
<Button isDefault onClick={ open } >
Bild setzen
</Button>
) }
/>
</div>
</Fragment>;
},
save: ( props ) => {
const {
attributes: {
text,
imageData
},
setAttributes,
className
} = props;
return <Fragment>
<div class="full-image-title-image-wrapper" {...imageData}>
<img class="full-image-title-image" src={imageData['thumbnail-url']} />
</div>{ (text)? <h2 class="full-image-title-title">{text}</h2> : ''}
</Fragment>;
},
} );
js (frontend)
if($('.full-image-title-outer-wrapper').length){
var w = window.innerWidth;
var h = window.innerHeight;
loadTitleImage();
$( window ).resize(function() { loadTitleImage(); });
$(window).scroll(function(){
$('.full-image-title-image-wrapper').each(function(index, el) {
if(el.getBoundingClientRect().y <= 0){
$(el).addClass('rollout')
}else {
$(el).removeClass('rollout')
}
});
});
}
function loadTitleImage(){
console.log('loadTitleImage');
$('.full-image-title-image-wrapper').each(function(index, el) {
let url = false;
$(['fhd','wqhd','uhd']).each(function(index, size) {
if($(el)[0].hasAttribute(size+'-url')) url = $(el).attr(size+'-url');
if(
$(el)[0].hasAttribute(size+'-url') &&
parseInt($(el).attr(size+'-width')) > w &&
parseInt($(el).attr(size+'-height')) > h
){
url = $(el).attr(size+'-url');
return false;
}
});
if(!url) url = $(el).attr('full-url');
$(el).find('.full-image-title-image').attr('src', url );
});
}
css
@import "../scss/vars.scss";
#wrapper{
.full-image-title-inner-wrapper,
.full-image-title-outer-wrapper{
display: block;
height: 150vh;
}
.full-image-title-outer-wrapper,
.full-image-title-inner-wrapper,
.full-image-title-image-wrapper{
position: relative;
display: block;
width: 100%;
padding: 0;
}
.full-image-title-outer-wrapper{
.full-image-title-inner-wrapper{
max-width: none;
.full-image-title-image-wrapper{
position: sticky;
top: 0;
overflow: hidden;
width: 100%;
max-height: 100vh;
z-index: -1;
.full-image-title-image{
position: relative;
display: block;
width: auto;
height: auto;
min-width: 100vw;
min-height: 100vh;
max-width: none;
max-height: none;
top: 50vh;
left: 50vw;
transform: translate(-50%, -50%);
}
&:after{
content: ' ';
background-color: $white;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: all .3s ease-in-out;
}
&.rollout{
&:after{
opacity: .8;
}
& + .full-image-title-title{
transform: translateY(-50%);
}
}
}
.full-image-title-title{
transform: translateY(0);
z-index: 10;
font-size: 8vw;
font-weight: 700;
text-align: center;
width: 100%;
max-width: 75vw;
margin: 0 auto;
transition: all .3s ease-in-out;
}
}
}
}
.full-image-title-preview{
width: 100%;
height: 320px;
background-position: center;
background-size: cover;
}