Skip to content

Commit

Permalink
🚀 RELEASE: let’s do it! 🕺
Browse files Browse the repository at this point in the history
  • Loading branch information
colorful-tones committed Jun 11, 2024
0 parents commit 6d9d9a5
Show file tree
Hide file tree
Showing 9 changed files with 431 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Operating system specific files
.DS_Store
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Animated timeline plugin

This WordPress plugin extends the core Group block to create an animated timeline experience with subtle animation and considerations for [`prefers-reduced-motion`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion) respecting a visitors preference for non-essential motion.

The final pattern is registered within the plugin, but it could also potentially be included directly in a custom WordPress theme. It can also be found on the WordPress Pattern Directory as [Vertical Timeline](https://wordpress.org/patterns/pattern/vertical-timeline/).

There are two possible styling variations: `animated-timeline` and `animated-timeline animated-timeline--circles`.

## How to use

1. Download this plugin as a zip (click on 'Code' and choose 'Download ZIP')
2. Place the un-zipped directory in your WordPress `wp-content/plugins` directory
3. Activate the plugin
4. Create a new post / page and add the 'Animated Timeline' pattern.
5. Save and preview the final animated timeline.
6. Try editing the Advanced -> Additional CSS Classes from `animated-timeline` to `animated-timeline animated-timeline--circles` for the overall pattern's parent Group block to see the circle variation.

Feel free to fork it and use it however you like!

## Changelog

### June 11, 2024 - v1.0.0

Initial launch.
106 changes: 106 additions & 0 deletions animated-timeline.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php
/**
* Plugin Name: Animated Timeline
* Description: Extends the Group block for an animating timeline effect.
* Requires at least: 6.5
* Requires PHP: 7.4
* Version: 1.0.0
* Author: Damon Cook
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: animated-timeline
*/

if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}


/**
* Registers the custom script and style for the timeline plugin.
*/
function animated_timeline_register_scripts() {

// Register the custom script to be used later.
wp_register_script(
'animated-timeline-script',
plugin_dir_url( __FILE__ ) . '/assets/scripts/core-blocks/group--animated-timeline.js',
array(),
'1.0.0',
true
);

// Register the custom style to be used later.
wp_register_style(
'animated-timeline-style',
plugin_dir_url( __FILE__ ) . '/assets/styles/core-blocks/group--animated-timeline.css',
array(),
'1.0.0',
);
}
add_action( 'wp_enqueue_scripts', 'animated_timeline_register_scripts' );

/**
* Modify the core Group block.
*
* @param string $block_content The block content about to be rendered.
*
* @return string The maybe modified block content.
*/
function animated_timeline_filter_group_content( $block_content ) {
$processor = new WP_HTML_Tag_Processor( $block_content );
$counter = 0;

// Check for the presence of the 'timeline' class.
if ( ! $processor->next_tag( array( 'class_name' => 'animated-timeline' ) ) ) {
return $block_content;
}

// Loop through each child block with the class name 'wp-block-column'.
while ( $processor->next_tag( array( 'class_name' => 'wp-block-column' ) ) ) {
$processor->add_class( 'animated__item' );
++$counter;

switch ( $counter ) {
case 1:
$processor->add_class( 'animated__item--first' );
break;
case 2:
$processor->add_class( 'animated__item--line' );
break;
case 3:
$processor->add_class( 'animated__item--last' );
$counter = 0;
break;
}
}

$block_content = $processor->get_updated_html();

// Enqueue the custom script and style.
wp_enqueue_script( 'animated-timeline-script' );
wp_enqueue_style( 'animated-timeline-style' );

// Return the maybe modified block content.
return $block_content;
}
add_filter( 'render_block_core/group', 'animated_timeline_filter_group_content', 10 );

/**
* Registers a block pattern for the timeline plugin.
*
* This function registers a block pattern for the timeline plugin. It checks if the pattern file exists and then registers the pattern using the `register_block_pattern` function.
*/
function animated_timeline_register_block_pattern() {
$pattern_file = plugin_dir_path( __FILE__ ) . '/patterns/animated-timeline.php';

if ( ! file_exists( $pattern_file ) ) {
return;
}

register_block_pattern(
'animated-timeline/animated-timeline',
require $pattern_file
);
}
add_action( 'init', 'animated_timeline_register_block_pattern' );
Binary file added assets/images/WP-6.3-Lionel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/WP-6.4-Shirley.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/WP-6.5-Regina.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions assets/scripts/core-blocks/group--animated-timeline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

/**
* Initializes an Intersection Observer to add the 'loaded' class to elements when they become visible in the viewport.
* The Intersection Observer is set up to observe elements with the class 'animated__item'.
*
* @listens DOMContentLoaded
*/
document.addEventListener( 'DOMContentLoaded', () => {
const els = document.querySelectorAll( '.animated__item' );

const observerOptions = {
root: null,
rootMargin: '0px',
threshold: 0.33,
};

/**
* Callback function for the Intersection Observer.
* Adds the 'loaded' class to the target element if it is intersecting.
*
* @param {IntersectionObserverEntry[]} entries - An array of IntersectionObserverEntry objects.
*/
function observerCallback( entries ) {
entries.forEach( ( entry ) => {
if ( entry.isIntersecting ) {
entry.target.classList.add( 'loaded' );
}
} );
}

const observer = new IntersectionObserver(
observerCallback,
observerOptions
);

els.forEach( ( el ) => observer.observe( el ) );
} );
150 changes: 150 additions & 0 deletions assets/styles/core-blocks/group--animated-timeline.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/* Establish the positioning context for the timeline. */
.animated-timeline > div {
position: relative;
}

.animated__item {
visibility: hidden;
}

.animated__item.loaded {
visibility: visible;
}

/* First column - initial state */
.animated-timeline .animated__item--first {
opacity: 0;
transform: translateY(25px);
transition: transform 0.5s, opacity 0.5s;
transition-delay: 0.3s;
}

/* Last column - initial state */
.animated-timeline .animated__item--last {
opacity: 0;
transform: translateY(45px);
transition: transform 0.7s, opacity 0.8s;
transition-delay: 0.5s;
}

/* First and last column - loaded state */
.animated-timeline .animated__item--first.loaded,
.animated-timeline .animated__item--last.loaded {
opacity: 1;
transform: translateY(0);
}

/**
* Vertical line animation
* The vertical line is a pseudo-element of the middle column.
*/

/* Establish positioning context */
.animated-timeline .animated__item--line {
position: relative;
visibility: hidden;
}

/* Vertical line - initial state */
.animated-timeline .animated__item--line::before {
background-color: inherit;
content: "";
display: block;
height: 1px;
inset: 0;
opacity: 0;
overflow: hidden;
position: absolute;
transition: height 1.5s, opacity 0.1s;
transition-delay: 0.1s;
transition-origin: top;
visibility: hidden;
width: 100%;
z-index: -1;
}

/* Vertical line - loaded state */
.animated-timeline .animated__item--line.loaded::before {
height: 100%;
opacity: 1;
visibility: visible;
}

/* Middle column - inner text - initial state */
.animated-timeline .animated__item--line > p {
opacity: 0;
transition: opacity 0.5s;
transition-delay: 1.15s;
visibility: hidden;
}

/* Middle column - inner text - loaded state */
.animated-timeline:not(.animated-timeline--circles) .animated__item--line.loaded > p {
opacity: 1;
visibility: visible;
}

/**
* Circle timeline
* The circle timeline is a variation of the default timeline.
*/
.animated-timeline--circles .animated__item--line > p {
opacity: 1;
position: relative;
visibility: hidden;
}

/* Create the circles */
.animated-timeline--circles .animated__item--line > p::after,
.animated-timeline--circles .animated__item--line > p::before {
background-color: inherit;
border-radius: 50%;
content: "";
display: block;
height: 1rem;
left: calc(50% - 0.5rem);
opacity: 0;
position: absolute;
top: calc(50% - 0.5rem);
transition: opacity 0.4s, transform 0.6s;
visibility: hidden;
width: 1rem;
}

/* Background circle - initial state */
.animated-timeline--circles .animated__item--line > p::after {
background: none;
box-shadow: 0 0 0 4px currentColor;
transform: scale(0);
transition-delay: 1.2s;
z-index: 1;
}

/* Background circle - loaded state */
.animated-timeline--circles .animated__item--line.loaded > p::after {
opacity: 0.4;
transform: scale(1);
visibility: visible;
}

/* Foreground circle - initial state */
.animated-timeline--circles .animated__item--line > p::before {
transition-delay: 1s;
z-index: 2;
}

/* Foreground circle - loaded state */
.animated-timeline--circles .animated__item--line.loaded > p::before {
opacity: 1;
visibility: visible;
}

@media (prefers-reduced-motion: reduce) {
.animated-timeline *,
.animated-timeline *::after,
.animated-timeline *::before {
opacity: 1 !important;
transition: none !important;
visibility: visible !important;
}
}
Loading

0 comments on commit 6d9d9a5

Please sign in to comment.