PluginProbe ʕ •ᴥ•ʔ
FrontBlocks for Gutenberg/GeneratePress / trunk
FrontBlocks for Gutenberg/GeneratePress vtrunk
trunk 0.2.0 0.2.1 0.2.2 0.2.3 0.2.4 0.2.5 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1.0 1.2.0 1.2.1 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 ci-artifacts
frontblocks / includes / Frontend / TextAnimation.php
frontblocks / includes / Frontend Last commit date
Animations.php 1 month ago BackButton.php 7 months ago BeforeAfter.php 1 month ago BlockPatterns.php 4 months ago Carousel.php 1 week ago ColumnsSameHeight.php 1 week ago ContainerEdgeAlignment.php 4 weeks ago Counter.php 1 month ago DownloadButton.php 1 week ago Events.php 4 weeks ago FaqSchema.php 1 week ago FluidTypography.php 4 weeks ago Gallery.php 8 months ago GravityFormsInline.php 1 month ago Headline.php 4 weeks ago InsertPost.php 1 month ago ProductCategories.php 4 weeks ago ReadingProgress.php 7 months ago ReadingTime.php 8 months ago ShapeAnimations.php 4 weeks ago StackedImages.php 4 months ago StickyColumn.php 4 weeks ago SvgUpload.php 4 weeks ago Testimonials.php 8 months ago TextAnimation.php 1 month ago UserText.php 4 weeks ago
TextAnimation.php
585 lines
1 <?php
2 /**
3 * Text Animation block for FrontBlocks.
4 *
5 * @package FrontBlocks
6 * @author Alex castellón <castellon@close.technology>
7 * @copyright 2025 Closemarketing
8 * @version 1.0.0
9 */
10
11 namespace FrontBlocks\Frontend;
12
13 defined( 'ABSPATH' ) || exit;
14
15 /**
16 * TextAnimation class.
17 *
18 * @since 1.3.0
19 */
20 class TextAnimation {
21
22 /**
23 * Constructor.
24 */
25 public function __construct() {
26 add_action( 'init', array( $this, 'register_block' ) );
27 add_action( 'enqueue_block_editor_assets', array( $this, 'register_text_animation_attributes' ), 5 );
28 add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_editor_assets' ) );
29 add_filter( 'render_block_frontblocks/text-animation', array( $this, 'maybe_enqueue_frontend_assets' ), 10, 2 );
30 add_filter( 'register_block_type_args', array( $this, 'register_native_block_attributes' ), 10, 2 );
31 add_filter( 'render_block_core/paragraph', array( $this, 'apply_text_animation_to_native_block' ), 10, 2 );
32 add_filter( 'render_block_core/heading', array( $this, 'apply_text_animation_to_native_block' ), 10, 2 );
33 }
34
35 /**
36 * Register the block type and its frontend style.
37 *
38 * @return void
39 */
40 public function register_block() {
41 wp_register_script(
42 'frontblocks-text-animation-editor',
43 FRBL_PLUGIN_URL . 'assets/text-animation/frontblocks-text-animation.js',
44 array(
45 'wp-blocks',
46 'wp-element',
47 'wp-editor',
48 'wp-block-editor',
49 'wp-components',
50 'wp-i18n',
51 'wp-data',
52 'wp-dom-ready',
53 ),
54 FRBL_VERSION,
55 true
56 );
57
58 wp_register_style(
59 'frontblocks-text-animation',
60 FRBL_PLUGIN_URL . 'assets/text-animation/frontblocks-text-animation.css',
61 array(),
62 FRBL_VERSION
63 );
64
65 wp_register_script(
66 'frontblocks-text-animation-frontend',
67 FRBL_PLUGIN_URL . 'assets/text-animation/frontblocks-text-animation-frontend.js',
68 array(),
69 FRBL_VERSION,
70 true
71 );
72
73 wp_register_script(
74 'frontblocks-animation-fade-in',
75 FRBL_PLUGIN_URL . 'assets/text-animation/animations/fade-in.js',
76 array( 'frontblocks-text-animation-frontend' ),
77 FRBL_VERSION,
78 true
79 );
80
81 wp_register_script(
82 'frontblocks-animation-typewriter',
83 FRBL_PLUGIN_URL . 'assets/text-animation/animations/typewriter.js',
84 array( 'frontblocks-text-animation-frontend' ),
85 FRBL_VERSION,
86 true
87 );
88
89 wp_register_script(
90 'frontblocks-animation-rotate-in',
91 FRBL_PLUGIN_URL . 'assets/text-animation/animations/rotate-in.js',
92 array( 'frontblocks-text-animation-frontend' ),
93 FRBL_VERSION,
94 true
95 );
96
97 wp_register_script(
98 'frontblocks-animation-flip-in',
99 FRBL_PLUGIN_URL . 'assets/text-animation/animations/flip-in.js',
100 array( 'frontblocks-text-animation-frontend' ),
101 FRBL_VERSION,
102 true
103 );
104
105 wp_register_script(
106 'frontblocks-animation-bounce-in',
107 FRBL_PLUGIN_URL . 'assets/text-animation/animations/bounce-in.js',
108 array( 'frontblocks-text-animation-frontend' ),
109 FRBL_VERSION,
110 true
111 );
112
113 wp_register_script(
114 'frontblocks-animation-glow-in',
115 FRBL_PLUGIN_URL . 'assets/text-animation/animations/glow-in.js',
116 array( 'frontblocks-text-animation-frontend' ),
117 FRBL_VERSION,
118 true
119 );
120
121 wp_register_script(
122 'frontblocks-animation-blur-in',
123 FRBL_PLUGIN_URL . 'assets/text-animation/animations/blur-in.js',
124 array( 'frontblocks-text-animation-frontend' ),
125 FRBL_VERSION,
126 true
127 );
128
129 wp_register_script(
130 'frontblocks-animation-scale-in',
131 FRBL_PLUGIN_URL . 'assets/text-animation/animations/scale-in.js',
132 array( 'frontblocks-text-animation-frontend' ),
133 FRBL_VERSION,
134 true
135 );
136
137 wp_register_script(
138 'frontblocks-animation-slide-up',
139 FRBL_PLUGIN_URL . 'assets/text-animation/animations/slide-up.js',
140 array( 'frontblocks-text-animation-frontend' ),
141 FRBL_VERSION,
142 true
143 );
144
145 wp_register_script(
146 'frontblocks-animation-slide-down',
147 FRBL_PLUGIN_URL . 'assets/text-animation/animations/slide-down.js',
148 array( 'frontblocks-text-animation-frontend' ),
149 FRBL_VERSION,
150 true
151 );
152
153 wp_register_script(
154 'frontblocks-animation-slide-left',
155 FRBL_PLUGIN_URL . 'assets/text-animation/animations/slide-left.js',
156 array( 'frontblocks-text-animation-frontend' ),
157 FRBL_VERSION,
158 true
159 );
160
161 wp_register_script(
162 'frontblocks-animation-slide-right',
163 FRBL_PLUGIN_URL . 'assets/text-animation/animations/slide-right.js',
164 array( 'frontblocks-text-animation-frontend' ),
165 FRBL_VERSION,
166 true
167 );
168
169 wp_register_script(
170 'frontblocks-animation-drop-in',
171 FRBL_PLUGIN_URL . 'assets/text-animation/animations/drop-in.js',
172 array( 'frontblocks-text-animation-frontend' ),
173 FRBL_VERSION,
174 true
175 );
176
177 wp_register_script(
178 'frontblocks-animation-swing',
179 FRBL_PLUGIN_URL . 'assets/text-animation/animations/swing.js',
180 array( 'frontblocks-text-animation-frontend' ),
181 FRBL_VERSION,
182 true
183 );
184
185 wp_register_script(
186 'frontblocks-animation-pulse',
187 FRBL_PLUGIN_URL . 'assets/text-animation/animations/pulse.js',
188 array( 'frontblocks-text-animation-frontend' ),
189 FRBL_VERSION,
190 true
191 );
192
193 wp_register_script(
194 'frontblocks-animation-flash',
195 FRBL_PLUGIN_URL . 'assets/text-animation/animations/flash.js',
196 array( 'frontblocks-text-animation-frontend' ),
197 FRBL_VERSION,
198 true
199 );
200
201 wp_register_script(
202 'frontblocks-animation-rubber-band',
203 FRBL_PLUGIN_URL . 'assets/text-animation/animations/rubber-band.js',
204 array( 'frontblocks-text-animation-frontend' ),
205 FRBL_VERSION,
206 true
207 );
208
209 wp_register_script(
210 'frontblocks-animation-wave',
211 FRBL_PLUGIN_URL . 'assets/text-animation/animations/wave.js',
212 array( 'frontblocks-text-animation-frontend' ),
213 FRBL_VERSION,
214 true
215 );
216
217 wp_register_script(
218 'frontblocks-animation-stretch',
219 FRBL_PLUGIN_URL . 'assets/text-animation/animations/stretch.js',
220 array( 'frontblocks-text-animation-frontend' ),
221 FRBL_VERSION,
222 true
223 );
224
225 wp_register_script(
226 'frontblocks-animation-squeeze',
227 FRBL_PLUGIN_URL . 'assets/text-animation/animations/squeeze.js',
228 array( 'frontblocks-text-animation-frontend' ),
229 FRBL_VERSION,
230 true
231 );
232
233 wp_register_script(
234 'frontblocks-animation-roll-in',
235 FRBL_PLUGIN_URL . 'assets/text-animation/animations/roll-in.js',
236 array( 'frontblocks-text-animation-frontend' ),
237 FRBL_VERSION,
238 true
239 );
240
241 wp_register_script(
242 'frontblocks-animation-glitch',
243 FRBL_PLUGIN_URL . 'assets/text-animation/animations/glitch.js',
244 array( 'frontblocks-text-animation-frontend' ),
245 FRBL_VERSION,
246 true
247 );
248
249 wp_register_script(
250 'frontblocks-animation-random-reveal',
251 FRBL_PLUGIN_URL . 'assets/text-animation/animations/random-reveal.js',
252 array( 'frontblocks-text-animation-frontend' ),
253 FRBL_VERSION,
254 true
255 );
256
257 wp_register_script(
258 'frontblocks-animation-flicker',
259 FRBL_PLUGIN_URL . 'assets/text-animation/animations/flicker.js',
260 array( 'frontblocks-text-animation-frontend' ),
261 FRBL_VERSION,
262 true
263 );
264
265 wp_register_script(
266 'frontblocks-animation-block-reveal',
267 FRBL_PLUGIN_URL . 'assets/text-animation/animations/block-reveal.js',
268 array( 'frontblocks-text-animation-frontend' ),
269 FRBL_VERSION,
270 true
271 );
272
273 wp_register_script(
274 'frontblocks-animation-tracking-expand',
275 FRBL_PLUGIN_URL . 'assets/text-animation/animations/tracking-expand.js',
276 array( 'frontblocks-text-animation-frontend' ),
277 FRBL_VERSION,
278 true
279 );
280
281 wp_register_script(
282 'frontblocks-animation-terminal-type',
283 FRBL_PLUGIN_URL . 'assets/text-animation/animations/terminal-type.js',
284 array( 'frontblocks-text-animation-frontend' ),
285 FRBL_VERSION,
286 true
287 );
288
289 wp_register_script(
290 'frontblocks-animation-solid-outline',
291 FRBL_PLUGIN_URL . 'assets/text-animation/animations/solid-outline.js',
292 array( 'frontblocks-text-animation-frontend' ),
293 FRBL_VERSION,
294 true
295 );
296
297 wp_register_script(
298 'frontblocks-animation-glitch-rgb',
299 FRBL_PLUGIN_URL . 'assets/text-animation/animations/glitch-rgb.js',
300 array( 'frontblocks-text-animation-frontend' ),
301 FRBL_VERSION,
302 true
303 );
304
305 wp_register_script(
306 'frontblocks-animation-water-drop',
307 FRBL_PLUGIN_URL . 'assets/text-animation/animations/water-drop.js',
308 array( 'frontblocks-text-animation-frontend' ),
309 FRBL_VERSION,
310 true
311 );
312
313 wp_register_script(
314 'frontblocks-animation-pulse-neon',
315 FRBL_PLUGIN_URL . 'assets/text-animation/animations/pulse-neon.js',
316 array( 'frontblocks-text-animation-frontend' ),
317 FRBL_VERSION,
318 true
319 );
320
321 wp_register_script(
322 'frontblocks-animation-shadow-pop',
323 FRBL_PLUGIN_URL . 'assets/text-animation/animations/shadow-pop.js',
324 array( 'frontblocks-text-animation-frontend' ),
325 FRBL_VERSION,
326 true
327 );
328
329 wp_register_script(
330 'frontblocks-animation-shuffle-text',
331 FRBL_PLUGIN_URL . 'assets/text-animation/animations/shuffle-text.js',
332 array( 'frontblocks-text-animation-frontend' ),
333 FRBL_VERSION,
334 true
335 );
336
337 register_block_type(
338 'frontblocks/text-animation',
339 array(
340 'editor_script' => 'frontblocks-text-animation-editor',
341 'editor_style' => 'frontblocks-text-animation',
342 'style' => 'frontblocks-text-animation',
343 )
344 );
345 }
346
347 /**
348 * Inject frblTextAnimation attribute into core blocks via inline script on wp-blocks.
349 *
350 * Runs at priority 5 (before wp-block-library registers core blocks) so the
351 * blocks.registerBlockType filter is in place when core/paragraph and core/heading
352 * are registered. This ensures the serializer includes the attribute in the block comment.
353 *
354 * @return void
355 */
356 public function register_text_animation_attributes() {
357 wp_add_inline_script(
358 'wp-blocks',
359 "
360 wp.hooks.addFilter(
361 'blocks.registerBlockType',
362 'frontblocks/text-animation-native-attrs',
363 function( settings, name ) {
364 if ( 'core/paragraph' !== name && 'core/heading' !== name ) {
365 return settings;
366 }
367 if ( ! settings || typeof settings !== 'object' ) {
368 return settings;
369 }
370 if ( ! settings.attributes || typeof settings.attributes !== 'object' ) {
371 settings.attributes = {};
372 }
373
374 settings.attributes = Object.assign( {}, settings.attributes, {
375 frblTextAnimation: { type: 'string', default: 'none' },
376 frblTextAnimationLoop: { type: 'boolean', default: false }
377 } );
378
379 var originalSave = settings.save;
380 settings.save = function( props ) {
381 var animation = props.attributes.frblTextAnimation;
382 if ( ! animation || 'none' === animation ) {
383 return originalSave( props );
384 }
385 var element = originalSave( props );
386 if ( ! element ) return element;
387 try {
388 var existingClass = ( element.props && element.props.className ) || '';
389 var extraProps = {
390 className: existingClass ? existingClass + ' frbl-text-animation' : 'frbl-text-animation',
391 'data-animation': animation
392 };
393 if ( props.attributes.frblTextAnimationLoop ) {
394 extraProps['data-animation-loop'] = '1';
395 }
396 return wp.element.cloneElement( element, extraProps );
397 } catch ( e ) {
398 return element;
399 }
400 };
401
402 return settings;
403 }
404 );
405 "
406 );
407 }
408
409 /**
410 * Register frblTextAnimation attribute server-side for paragraph and heading.
411 *
412 * @param array $args Block type args.
413 * @param string $block_type Block type name.
414 * @return array
415 */
416 public function register_native_block_attributes( $args, $block_type ) {
417 if ( 'core/paragraph' !== $block_type && 'core/heading' !== $block_type ) {
418 return $args;
419 }
420
421 if ( ! isset( $args['attributes'] ) ) {
422 $args['attributes'] = array();
423 }
424
425 $args['attributes']['frblTextAnimation'] = array(
426 'type' => 'string',
427 'default' => 'none',
428 );
429
430 $args['attributes']['frblTextAnimationLoop'] = array(
431 'type' => 'boolean',
432 'default' => false,
433 );
434
435 return $args;
436 }
437
438 /**
439 * Add frbl-text-animation class and data-animation attr to paragraph/heading.
440 *
441 * @param string $block_content Block HTML.
442 * @param array $block Block data.
443 * @return string
444 */
445 public function apply_text_animation_to_native_block( $block_content, $block ) {
446 // Primary path: save() already wrote the class+data-animation into the HTML.
447 if ( false !== strpos( $block_content, 'frbl-text-animation' ) ) {
448 if ( preg_match( '/data-animation="([^"]+)"/', $block_content, $m ) ) {
449 $this->enqueue_animation_scripts( sanitize_text_field( $m[1] ) );
450 }
451 return $block_content;
452 }
453
454 // Fallback: attribute stored in block comment (older saves or edge cases).
455 $animation = isset( $block['attrs']['frblTextAnimation'] ) ? sanitize_text_field( $block['attrs']['frblTextAnimation'] ) : 'none';
456 if ( 'none' === $animation || '' === $animation ) {
457 return $block_content;
458 }
459
460 $block_content = preg_replace_callback(
461 '/^<([a-z][a-z0-9]*)\s*((?:[^>]|\n)*?)(?:style="([^"]*?)")?([^>]*?)>/i',
462 function ( $matches ) use ( $animation ) {
463 $tag = $matches[1] ?? 'div';
464 $before = $matches[2] ?? '';
465 $style = $matches[3] ?? '';
466 $after = $matches[4] ?? '';
467
468 if ( strpos( $before . $after, 'class="' ) !== false ) {
469 $before = preg_replace(
470 '/class="([^"]*)"/',
471 'class="$1 frbl-text-animation"',
472 $before . $after,
473 1
474 );
475 $after = '';
476 } else {
477 $before .= ' class="frbl-text-animation"';
478 }
479
480 $before .= ' data-animation="' . esc_attr( $animation ) . '"';
481
482 if ( ! empty( $style ) ) {
483 return '<' . $tag . ' ' . $before . ' style="' . $style . '">';
484 }
485
486 return '<' . $tag . ' ' . $before . '>';
487 },
488 $block_content,
489 1
490 );
491
492 $this->enqueue_animation_scripts( $animation );
493 return $block_content;
494 }
495
496 /**
497 * Enqueue the frontend scripts for a given animation type.
498 *
499 * @param string $animation Animation type slug.
500 * @return void
501 */
502 private function enqueue_animation_scripts( $animation ) {
503 $script_map = array(
504 'fade-in' => 'frontblocks-animation-fade-in',
505 'blur-in' => 'frontblocks-animation-blur-in',
506 'glow-in' => 'frontblocks-animation-glow-in',
507 'bounce-in' => 'frontblocks-animation-bounce-in',
508 'flip-in' => 'frontblocks-animation-flip-in',
509 'rotate-in' => 'frontblocks-animation-rotate-in',
510 'typewriter' => 'frontblocks-animation-typewriter',
511 'shuffle-text' => 'frontblocks-animation-shuffle-text',
512 'slide-up' => 'frontblocks-animation-slide-up',
513 'slide-down' => 'frontblocks-animation-slide-down',
514 'slide-left' => 'frontblocks-animation-slide-left',
515 'slide-right' => 'frontblocks-animation-slide-right',
516 'drop-in' => 'frontblocks-animation-drop-in',
517 'swing' => 'frontblocks-animation-swing',
518 'pulse' => 'frontblocks-animation-pulse',
519 'flash' => 'frontblocks-animation-flash',
520 'rubber-band' => 'frontblocks-animation-rubber-band',
521 'wave' => 'frontblocks-animation-wave',
522 'stretch' => 'frontblocks-animation-stretch',
523 'squeeze' => 'frontblocks-animation-squeeze',
524 'roll-in' => 'frontblocks-animation-roll-in',
525 'glitch' => 'frontblocks-animation-glitch',
526 'random-reveal' => 'frontblocks-animation-random-reveal',
527 'flicker' => 'frontblocks-animation-flicker',
528 'block-reveal' => 'frontblocks-animation-block-reveal',
529 'tracking-expand' => 'frontblocks-animation-tracking-expand',
530 'terminal-type' => 'frontblocks-animation-terminal-type',
531 'solid-outline' => 'frontblocks-animation-solid-outline',
532 'glitch-rgb' => 'frontblocks-animation-glitch-rgb',
533 'water-drop' => 'frontblocks-animation-water-drop',
534 'pulse-neon' => 'frontblocks-animation-pulse-neon',
535 'shadow-pop' => 'frontblocks-animation-shadow-pop',
536 'scale-in' => 'frontblocks-animation-scale-in',
537 );
538
539 if ( ! wp_style_is( 'frontblocks-text-animation', 'enqueued' ) ) {
540 wp_enqueue_style( 'frontblocks-text-animation' );
541 }
542
543 if ( ! wp_script_is( 'frontblocks-text-animation-frontend', 'enqueued' ) ) {
544 wp_enqueue_script( 'frontblocks-text-animation-frontend' );
545 }
546
547 if ( isset( $script_map[ $animation ] ) && ! wp_script_is( $script_map[ $animation ], 'enqueued' ) ) {
548 wp_enqueue_script( $script_map[ $animation ] );
549 }
550 }
551
552 /**
553 * Enqueue frontend JS only when block with animation is on the page.
554 *
555 * @param string $block_content Block HTML.
556 * @param array $block Block data.
557 * @return string
558 */
559 public function maybe_enqueue_frontend_assets( $block_content, $block ) {
560 $animation = $block['attrs']['animationType'] ?? 'none';
561
562 if ( 'none' === $animation ) {
563 return $block_content;
564 }
565
566 $this->enqueue_animation_scripts( $animation );
567
568 return $block_content;
569 }
570
571 /**
572 * Enqueue editor-only assets.
573 *
574 * @return void
575 */
576 public function enqueue_editor_assets() {
577 wp_enqueue_script( 'frontblocks-text-animation-editor' );
578
579 wp_set_script_translations(
580 'frontblocks-text-animation-editor',
581 'frontblocks'
582 );
583 }
584 }
585