lukePeavey / SplitType

Javascript utility that splits text into lines, words, characters for animation
https://lukepeavey.github.io/SplitType/
572 stars 40 forks source link

How can I wrap each generated div line with a span then revert upon finishing the animation? #21

Closed Doxxxie closed 2 years ago

Doxxxie commented 3 years ago

I've been playing with this library today and I'm beyond impressed by how simple & reliable it is to split typographic elements! However, there are 2 things that I'm currently struggling with at the moment:

  1. How can I wrap each generated div element with a span tag? The reason for this is to re-create this scroll-triggered animation using the overflow: hidden trick --> https://codepen.io/Ahmad_Mansour/pen/rNzVYbq?editors=0010
  2. How can I revert the split lines, words & chars after executing the animation because I'm not sure how to approach that.

This is the sandbox that I re-created so you don't have to start from scratch for this particular case. --> https://codepen.io/Ahmad_Mansour/pen/PoKNZqo?editors=0010

Thanks in advance!

StevenJPx2 commented 2 years ago
const el = document.querySelector(/*...*/); 
const instance = new SplitText(el, {types: 'lines'});

instance.lines.map(line => {
  const wrapEl = document.createElement('div');
  wrapEl.classList.add('overflow-hidden');
  // or wrapEl.style.overflow = 'hidden'
  line.parentNode.appendChild(wrapEl)
  wrapEl.appendChild(line)
})

Hope this helps!

lukePeavey commented 2 years ago

@Doxxxie Sorry about the slow response...

1. how to wrap each generated div in a span tag:

See @StevenJPx2's comment above.

2. how to revert split text back to its original state:

You can use the revert() method on the SplitType instance. This will "un-split" the target elements and revert them to their original HTML content.

const el = document.querySelector(/*...*/); 
const instance = new SplitText(el, {types: 'lines'});

// After your animations are complete...
instance.revert()
lukePeavey commented 2 years ago

I think this has been answered, so I'm going to close the now. @Doxxxie feel free to follow up if you have additional questions.

adamjw3 commented 9 months ago

i tried this fix in a next js project and it removes the spacing between words

Screenshot 2024-01-19 at 09 10 28
import React, { useEffect, useRef } from "react";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import SplitType from "split-type";

interface SplitProps {
  children: React.ReactNode;
  splitMode: String;
  delay: number;
}

const Split = ({ children, splitMode, delay = 0 }: SplitProps) => {
  const trigger = useRef<HTMLDivElement>(null);
  const target = useRef<HTMLDivElement>(null);

  useEffect(() => {
    gsap.registerPlugin(ScrollTrigger);

    const init = () => {
      split();
    };

    const animate = (text) => {
      gsap.from(text, {
        delay: delay,
        y: "105%",
        duration: 0.5,
        stagger: 0.05,
        ease: "power2.in",
        force3D: true,
        scrollTrigger: {
          trigger: trigger.current,
          scrub: false,
        },
      });
    };

    const splitChars = () => {
      const text = new SplitType(target.current!, { types: "chars" });

      text.chars!.map((char) => {
        const wrapEl = document.createElement("div");
        wrapEl.classList.add("inline-block", "overflow-hidden", "kerning-none");
        // or wrapEl.style.overflow = 'hidden'
        char!.parentNode!.appendChild(wrapEl);
        wrapEl.appendChild(char);
      });

      animate(text.chars);
    };

    const splitLines = () => {
      const text = new SplitType(target.current!, { types: "lines" });
      animate(text.lines);
    };

    const splitWords = () => {
      const text = new SplitType(target.current!, { types: "words" });
      animate(text.words);
    };

    const split = () => {
      if (splitMode === "chars") {
        splitChars();
      } else if (splitMode === "lines") {
        splitLines();
      } else if (splitMode === "words") {
        splitWords();
      }
    };

    init();
  }, []); // Empty dependency array ensures that the effect runs once after the initial render

  return (
    <span ref={trigger} className="inline-block">
      <span ref={target} className="inline-block overflow-hidden overflow-clip kerning-none">
        {children}
      </span>
    </span>
  );
};

export default Split;
adamjw3 commented 9 months ago

this fixed it for me

const wrapEl = document.createElement("span");
        wrapEl.classList.add("inline-block", "relative", "overflow-hidden");
        item!.parentNode!.insertBefore(wrapEl, item.nextSibling);
        wrapEl.appendChild(item);