首页 / 中文教程 / 教程

如何在 Bricks 中用 RoughNotation.js 实现滚动标注动画

本教程提供可直接粘贴的 PHP 与 JS 代码,让你在 Brickshttps://brickslabs.com/go/bricks Builder 里用 RoughNotation 库https://github.co…

Ray Chan·2026-08-12·约 4 分钟

本教程提供可直接粘贴的 PHP 与 JS 代码,让你在 Bricks Builder 里用 RoughNotation 库实现「滚动到元素时触发手绘风格标注动画」——高亮、下划线、方框、圆圈、删除线等效果。

目录

环境要求

JavaScript

首先在子主题里创建 init.js 文件。本例在子主题的 /js/ 文件夹下创建了 roughNotation_init.js,内容如下:

import { annotate } from 'https://unpkg.com/rough-notation?module';
window.addEventListener('load', () =>{
    const annotations = {
        'highlight-1': { type: 'highlight', color: '#fcc2d2', iterations: 4, multiline: true, animate: true },
        'highlight-2': { type: 'highlight', color: 'orange', iterations: 4, multiline: true, animate: true },
        'underline': { type: 'underline', color: 'yellow', iterations: 4, multiline: true, animate: true },
        'box': { type: 'box', color: 'purple', iterations: 2, multiline: true, animate: true },
        'circle': { type: 'circle', color: 'green', iterations: 1, multiline: true, animate: true },
        'strike-through': { type: 'strike-through', color: 'red', iterations: 1, multiline: true, animate: true },
        'crossed-off': { type: 'crossed-off', color: 'black', iterations: 2, multiline: true, animate: true },
        'bracket': { type: 'bracket', brackets: ['left','right'], color: 'blue', iterations: 2, multiline: true, animate: true },
    };  
    // Set the variables
    var iOSupported = "IntersectionObserver" in window; /* true if supported */
    var box = document.querySelectorAll('.notation');
    
    // Check if IntersectionObserver is supported by the browser
    if (!iOSupported) {
        return;
    }

    // Set the config options 
    const config = {
        root: null, // sets the framing element to the viewport
        rootMargin: '0% 0% -25% 0%', // the animation will be triggered when at 25% from the bottom of the viewport
        threshold: 0 
    };
    
    // Init the observer
    let observer = new IntersectionObserver((entries) => {
        entries.forEach((item) => {
            let annotationType = item.target.dataset.notationType;
            let annotation = annotate(item.target, annotations[annotationType]);
        
            if (item.isIntersecting) {
                // Show the annotation when intersecting and stop observing after it.
                annotation.show();
                observer.unobserve(item.target);

            } else {
                // Add an action when the target is not intersecting anymore.

            }    
        });  
    }, config);

    box.forEach((item) => {
        observer.observe(item);
    });
})

脚本分两个部分:

第一部分是定义动画样式对象的 annotations 常量——每种标注类型可配置 typecoloriterationspadding 等大量选项,完整选项列表见作者的项目主页。为了演示全部标注类型,示例定义了以下动画:

const annotations = {
        'highlight-1': { type: 'highlight', color: '#fcc2d2', iterations: 4, multiline: true, animate: true },
        'highlight-2': { type: 'highlight', color: 'orange', iterations: 4, multiline: true, animate: true },
        'underline': { type: 'underline', color: 'yellow', iterations: 4, multiline: true, animate: true },
        'box': { type: 'box', color: 'purple', iterations: 2, multiline: true, animate: true },
        'circle': { type: 'circle', color: 'green', iterations: 1, multiline: true, animate: true },
        'strike-through': { type: 'strike-through', color: 'red', iterations: 1, multiline: true, animate: true },
        'crossed-off': { type: 'crossed-off', color: 'black', iterations: 2, multiline: true, animate: true },
        'bracket': { type: 'bracket', brackets: ['left','right'], color: 'blue', iterations: 2, multiline: true, animate: true },
};

第二部分负责滚动触发动画,用的是 IntersectionObserver API,非常轻量(方法同这篇教程):

// Set the variables
    var iOSupported = "IntersectionObserver" in window; /* true if supported */
    var box = document.querySelectorAll('.notation');
    
    // Check if IntersectionObserver is supported by the browser
    if (!iOSupported) {
        return;
    }

    // Set the config options 
    const config = {
        root: null, // sets the framing element to the viewport
        rootMargin: '0% 0% -25% 0%', // the animation will be triggered when at 25% from the bottom of the viewport
        threshold: 0 
    };
    
    // Init the observer
    let observer = new IntersectionObserver((entries) => {
        entries.forEach((item) => {
            let annotationType = item.target.dataset.notationType;
            let annotation = annotate(item.target, annotations[annotationType]);
        
            if (item.isIntersecting) {
                // Show the annotation when intersecting and stop observing after it.
                annotation.show();
                observer.unobserve(item.target);

            } else {
                // Add an action when the target is not intersecting anymore.

            }      
        });     
    }, config);

    box.forEach((item) => {
        observer.observe(item);
    });

配置里 rootMargin: '0% 0% -25% 0%' 表示元素进入视口底部 25% 区域时才触发动画。

加载脚本

RoughNotation 库走的是 JavaScript ES module,所以加载方式比常规脚本略特殊——需要给 <script> 标签加上 type="module"。把下面代码粘贴到子主题的 functions.php

add_action( 'wp_enqueue_scripts', function() {
	// Enqueue your files on the canvas & frontend, not the builder panel. Otherwise custom CSS might affect builder)
   if ( ! bricks_is_builder_main() && class_exists( 'ACF' ) ) {
  
      // Rough Notation
      if ( get_field( 'activate_roughnotationjs' ) ) {
    	    wp_enqueue_script( 'roughNotation_init', get_stylesheet_directory_uri() . '/js/roughNotation_init.js', array(), filemtime( get_stylesheet_directory() . '/js/roughNotation_init.js' ) );
      }
   }
});

// Add type="module" to our script tag
function add_type_attribute( $tag, $handle, $src ) {
    // if not your script, do nothing and return original $tag
    if ( 'roughNotation_init' !== $handle ) {
        return $tag;
    }
    // change the script tag by adding type="module" and return it.
    $tag = '<script type="module" src="' . esc_url( $src ) . '"></script>';
    return $tag;
}
add_filter( 'script_loader_tag', 'add_type_attribute' , 10, 3 );

💡 译者注:原文此处爬取文本里混入了 markdown 链接残留(class_exists( '[ACF](https://brickslabs.com/go/acf)' )),已按实际用途修正为 class_exists( 'ACF' )

这里还用了一个 ACF 字段(activate_roughnotationjs)按页控制是否加载脚本,方法见这篇教程

HTML 代码

通常我们只想标注标题(Heading)、基础文本(Basic)或富文本(Rich)元素里的部分文字。做法:把目标文字包进 <span> 标签,加上触发滚动动画的类,并指定要运行的动画类型。下面是对标题应用高亮效果的示例:

I am an awesome <span class="notation" data-notation-type="highlight-1">heading</span>
  • .notation 类负责让 IntersectionObserver 脚本盯住这个元素;
  • data-notation-type="highlight-1" 属性告诉脚本使用前面 annotations 常量中定义的 highlight-1 配置。

如果打算标注整个元素而不是其中一部分,就不需要包 <span>——直接用 Bricks 内置字段给元素添加 .notation 类和 data-notation-type 属性即可。

结论

一切正常的话,前端效果就是滚动到标注文字时出现手绘风格的涂抹高亮动画。祝标注愉快,下篇教程见!

需要帮忙?

用 Bricks 建站?我接客户项目。

从快速营销站到完整的 Bricks 建站,再到从 Elementor 迁移——我都做过。本站每篇教程都来自真实项目经验。告诉我你的需求,一个工作日内回复。

  • Bricks 建站与改版
  • Elementor / Divi → Bricks 迁移
  • Bricks → Astro / headless 性能升级
  • 速度优化,PageSpeed 95+ 目标