こんにちは、ノベルティの山本です。
今回は、文字を1文字ずつアニメーションさせたり、1行ずつアニメーションさせるJSコードを紹介します。
このアニメーションはスクロールに応じて発火させたり、ページ遷移後ファーストビューのタイトル部分に適用するといい感じになるのではないかと思います。

はじめに

こちらのコードは、ソースコードをどこから引っ張ってきたか覚えていないうえに、山本自身もちゃんとコードを読み解いていないため、いい感じにアニメーションするから「とりあえず使っている」コードになります。
それに加えて、アニメーション自体はTweenMaxというライブラリを使用している為、事前にダウンロードしてサイトに読み込ませておいてください。
基本的に山本が制作するサイトのアニメーションはTweenMaxを使用してアニメーションさせています。
CSSでアニメーションさせると、処理の遅い端末で思った様に動かないので基本的にJSでアニメーションさせるようにしています。
このコードを使うと自由度が高いため様々なテキストアニメーションを付けられるようになりますが、TweenMaxの概要を理解していないとアレンジできなくなってしまうので一応軽く解説しておきたいと思います。

スプリットテキストのJSコード

(function($){ 
	$.fn.splitText = function(options) {
		var opts = {
			'type' : 'lines',
			'animation' : 'explode',
			'justSplit' : false,
			'duration' : 1.0,
			'scale' : true,
			'useLite' : false,
			'colorize' : null,
			'useCSS' : false
		};
		if(options == null ||
			options == undefined ||
			options == '' ||
			(options.type !== 'words' &&
			options.type !== 'lines' &&
			options.type !== 'letters' &&
			options.type !== 'sentences')
		) {
			options = opts;
		}
		if(options.duration == undefined) {
			options.duration = 1.0;
		}
		var element = $(this);
		if(element.hasClass('isSplit')) {
			element.empty();
			element.text($('#hidden_'+element.attr('id')).text());
		}
		else {
			element.attr('id',String(Math.round(Math.random()*1000+42)));
			element.addClass('isSplit');
		}
		var userInput = element.text();
		var TMax = options.useLite == false?new TimelineMax():new TimelineLite();
		var initialText = element.text();
		var hiddenId = $('#hidden_'+ element.attr('id'));
		var parentID = "hidden_"+ element.attr('id');
		if(options.type=='lines') {
			var result = splitWords(userInput);
			element.html(result);
			var obj = splitLines();
			if(options.justSplit == true){
				return {'id':element.attr('id'),'value':obj};
			}
			element.empty();
			$.each(obj,function(index,value) {
				var item = "<div class='split-lines'>"+value.text+"</div>";
				element.append(item);
			});
		}
		else if(options.type=='words') {
			var result = splitWords(initialText);
			if(options.justSplit == true) {
				return {'id':element.attr('id'),'value':result};
			}
			element.empty();
			element.html(result);
		}
		else if(options.type=='letters') {
			var result = splitLetters(initialText);
			if(options.justSplit == true) {
				return {'id':element.attr('id'),'value':result};
			}
			element.empty();
			element.html(result);
		}
		else if(options.type == 'sentences') {
			var result  = splitSentences(initialText);
			if(options.justSplit == true){
				return { 'id':element.attr('id'), 'value':result };
			}
			element.empty();
			element.html(result);
		}	
		function splitLetters(userInput) {
			var arr = userInput.split("");
			for (var i=0;i<arr.length;i++) { 
				if (arr[i] == " ") {
                    arr[i] = '<div class="letter-measure blank">' + arr[i] + '</div>';
				}
				else {
		      		if(!arr[i].match(/\s\n\t\r/g) && arr[i]!="") arr[i] = '<div class="letter-measure">' + arr[i] + '</div>';
		     	}
		   }
		   return arr.join(" ");
		}
		function splitWords(userInput, justSplit) {
			  var a = userInput.replace(/\n/g, " \n<br/> ").split(" ");
   			   if(justSplit == true) {
   			   		$.each(a, function(i, val) { 
			      		if(!val.match(/\n/) && val!="") a[i] = val;
			   		});
			   		return a;
   			   }
			   $.each(a, function(i, val) { 
			      if(!val.match(/\n/) && val!="") a[i] = '<div class="word-measure">' + val + '</div>';
			   });
			   var arr = a.join(" ");
			   return arr;	
		}
		function splitLines(userInput) {
            var count = element.children(".word-measure").length;
            var lineAcc = [element.children(".word-measure:eq(0)").text()];
            var textAcc = [];
            for(var i = 1; i < count; i++) {
			        var prevY = element.children(".word-measure:eq("+(i-1)+")").offset().top;
                if(element.children(".word-measure:eq("+i+")").offset().top==prevY){
                        lineAcc.push(element.children(".word-measure:eq("+i+")").text());
                } 
                else {
                    textAcc.push({text: lineAcc.join(" "), top: prevY});
                    lineAcc = [element.children(".word-measure:eq("+i+")").text()];
                }
		   }
		   textAcc.push({
				text: lineAcc.join(" "),
				top: element.children(".word-measure:last").offset().top
			});
		   return textAcc;
		}
		function splitSentences(userInput) {
            var regExp = /[^\.!\?]+[\.!\?]+/g;
            var words = splitWords(userInput,true);
            var sentencesArr = String(userInput).match(regExp);
            var textAcc = new Array();
            for (var i = 0; i < sentencesArr.length; i++) {
                textAcc.push({'text' : sentencesArr[i]});
            }
            console.log(words);
            textAcc = new Array();
            for (var j = 0; j < words.length; j++) {
            var word = words[j];
            isSentenceEnd = regExp.test(word);
            if (isSentenceEnd) {
                words[j] = "<div class='split-sentences endOfSentence'>" + word + "</div>";
            }
            else {
                words[j] = "<div class='split-sentences'>" + word + "</div>";
            }
            textAcc.push(words[j]);
            }
            var arr = words.join(" ");
            return arr;
		}	
		return this;
	};	
})(jQuery);

正規表現をマスターしていないため、いまいちコードの内容分かっていません。とりあえずこのコードを読み込ませておけばテキストを自動的に小分けする準備が整います。
ちなみにTweenMaxでも課金すればスプリットテキストを使うことが出来るようになります。このコードは課金しなくてもスプリットテキストを使えるようにしたコードになります。

アニメーションさせるためのJSコード

$(".split-text-lines").each(function() {
    $(this).splitText({
        'type': 'lines',
        'useLite': true
    });
});
$(".split-text").each(function() {
	$(this).splitText({
		'type': 'letters',
		'useLite': true,
	});
});
var splitText = [].slice.call(document.querySelectorAll(".split-text"));
for (var n in splitText) {
	TweenMax.set(splitText[n].children, {
		opacity: 0,
		y: 40,
		color: "#f8d8c8"
	})
}

まず、テキストを分割したい箇所を指定して分割させます。
一番上の.split-text-linesは今回使用しませんが、参考のために紹介しておきます。
.splitTextの’type’箇所にどのような処理をしたいか記述します。
linesは一行ごとに分割する、wordsは単語ごとに分割する、lettersは1文字ごとに分割させます。
今回は一番需要があるであろう1文字ごとに分割させるlettersを使用します。

一番下にある、for文で.split-textで分割された子要素に繰り返しの処理をさせています。
今回は、下から一文字ずつフェードインしてくるアニメーションを付けたいためopacityを0にして…(以下省略)をしてTweenMaxのsetを使ってあらかじめ値をその名の通りセットしておきます。

function splitTextRotationAnimation() {
    var splitText = [].slice.call(document.querySelectorAll(".split-text"));
    const options = {
        root: null,
        threshold: [0, 0.2, 0.4, 0.6, 0.8, 1]
    }
    if ("IntersectionObserver" in window) {
        const splitTextObserver = new IntersectionObserver(function(entries, observer) {
            entries.forEach(function(entry) {
                if (entry.intersectionRatio > 0.2) {
                    let splitText = entry.target;
                    TweenMax.staggerTo(splitText.children, .5, {
                        opacity: 1,
                        y: 0,
                        ease: Elastic.easeOut.config(1, 1),
                        delay: .1
                    }, .075),
                    TweenMax.staggerTo(splitText.children, .5, {
                        css: {
                            color: "#f798a5",
                        },
                        delay: .4,
                        ease: Power4.easeOut
                    }, .075);
                    splitText.classList.remove("split-text");
                    splitTextObserver.unobserve(splitText);
                }
            });
        }, options);
        splitText.forEach(function(el) {
            splitTextObserver.observe(el);
        });
    } else {
    }
}
splitTextRotationAnimation();

上記コードで、要素の位置までスクロールした際にアニメーションが発火するようにしています。
例のごとく、スクロールの判定はIntersectionObserverを使用しています。

TweenMaxのstaggerToを使用して.splitTextの子要素を1文字ずつ時間差でアニメーションさせるように設定しています。
簡単ですが、以上で説明を終わりにしたいと思います。

cssは特に難しいことをしていないため、適当に記述してもらえればと思います。
ちなみにTweenMaxのsetでアニメーションさせる前の記述をして、staggerToでアニメーション後の値を指定しているだけなのでその値をそれぞれ自由に変えていただければ色々なアニメーションが作れます。

PAGE TOP