【javascript】スクロールしたら吹き出しが現れ、一文字ずつアニメーション
sample
以下のように、要素が下から画面の20%スクロールされたら、吹き出しが現れ、 1文字ずつアニメーションさせるコードを書きました。参考にしてください。
HTML
HTMLは2行だけ。
要素が現れる目安となる線がいらなければ、
<div class="border"></div>
はいりません。
<div class="border"></div> <!-- 省略可 -->
<div class="content"></div>
css
画像と吹き出しの部分
.wrapper {
display: flex;
margin: 10px;
padding: 10px;
opacity: 0;
transition: all 1s ease;
}
.wrapper.active { /* 下から画面20%分スクロールされたactiveを追加する。 */
animation: appear 2s forwards;
}
@keyframes appear {
0% {
opacity: 0;
transform: translateY(50px);
}
100% {
opacity: 1;
transform: none;
}
}
.wrapper:nth-of-type(even) { /* 偶数分には画像と吹き出しの順序を入れ替え */
flex-direction: row-reverse;
}
.wrapper:nth-of-type(even).content .wrapper img {
margin-left: 40px;
}
吹き出し部分
吹き出しは全て自分で作るのはしんどいので、ジェネレーターを使うと便利です。
👉 吹き出しジェネレーター .personA,
.personB {
position: relative;
max-width: 500px;
height: 100%;
padding: 10px;
text-align: left;
line-height: 1em;
color: #ffffff;
font-size: 20px;
font-weight: bold;
border-radius: 15px;
-webkit-border-radius: 15px;
-moz-border-radius: 15px;
text-shadow: 1px 1px 2px #111;
}
.personA {
background: green;
border: 2px solid #666;
margin-top: 10px;
margin-left: 30px;
}
.personB {
background: rgb(255, 123, 0);
border: 2px solid #9b350a;
margin-top: 10px;
margin-right: 30px;
}
.personA:after,
.personA:before,
.personB:after,
.personB:before {
border: solid transparent;
content: "";
height: 0;
width: 0;
pointer-events: none;
position: absolute;
top: 50%;
}
.personA:after,
.personA:before{
right: 100%;
}
.personB:after,
.personB:before {
left: 100%;
}
.personA::after,
.personB::after{
border-top-width: 10px;
border-bottom-width: 10px;
border-left-width: 20px;
border-right-width: 20px;
margin-top: -10px;
}
.personA:after {
border-color: rgba(76, 238, 81, 0);
border-right-color: green;
}
.personB:after {
border-color: rgba(238, 181, 72, 0);
border-left-color: rgb(255, 123, 0);
}
.personA:before,
.personB:before {
border-top-width: 12px;
border-bottom-width: 12px;
border-left-width: 24px;
border-right-width: 24px;
margin-top: -12px;
}.personA:before {
border-color: rgba(56, 155, 12, 0);
margin-right: 2px;
border-right-color: #666;
}
.personB:before {
border-color: rgba(155, 53, 10, 0);
margin-left: 2px;
border-left-color: rgb(104, 67, 0);
}
1文字ずつアニメーション
@keyframes slide {
0% {
transform: translateX(-50%);
opacity: 0;
}
20% {
transform: translateX(0px);
opacity: 1;
}
}
.wrapper.active > .textAni > span > span {
animation: slide 0.5s ease-in-out backwards;
}
.textAni span {
display: inline-block;
overflow: hidden;
}
javascript
data.js
const pA = "./demo/conversation/img/nanasi.png",
pB = "./demo/conversation/img/nanasi.png"
const data = [
{
img: pA,
text: "ミラーさん。",
},
{
img: pB,
text: "何ですか。",
},
{
img: pA,
text: "明日友達とお花見をします。ミラーさんも一緒に行きませんか?",
},
{
img: pB,
text: "いいですね。どこへ行きますか。",
},
{
img: pA,
text: "大阪城公園です。",
},
{
img: pB,
text: "何時ですか。",
},
{
img: pA,
text: "10時です。大阪城公園駅で会いましょう。",
},
{
img: pB,
text: "わかりました。",
},
{
img: pA,
text: "じゃ、また明日",
},
];
HTML生成
const content = document.querySelector(".content");
const animationTarget = document.querySelectorAll(".textAni");
for (let i = 0; i < data.length; i++) {
const element = data[i];
const div = document.createElement("div");
const img = document.createElement("img");
const div2 = document.createElement("div");
div.classList.add("wrapper");
img.src = element.img;
if (i % 2 == 0) {
div2.classList.add("personA");
} else {
div2.classList.add("personB");
}
div2.classList.add("textAni");
content.appendChild(div);
div.appendChild(img)
div.appendChild(div2);
const texts = element.text;
textsArray = [];
for (let j = 0; j < texts.split("").length; j++) {
const textT = texts.split("")[j];
if (textT === " ") {
textsArray.push(" ");
} else {
textsArray.push(
'<span><span style="animation-delay:' +
(j * 0.1 + 0.2) +
's;">' +
textT +
"</span></span>"
);
}
}
for (let k = 0; k < textsArray.length; k++) {
div2.innerHTML += textsArray[k];
}
}
スクロールしたらアニメーション
window.addEventListener("scroll", function () {
for (let i = 0; i < wrapper.length; i++) {
const element = wrapper[i];
const distance = element.getBoundingClientRect().top +
window.innerHeight * 0.2;
// 下から画面20%分、要素が現れたら、クラスactiveを追加
if (window.innerHeight > distance) {
element.classList.add("active");
} else {
element.classList.remove("active");
}
}
});
参考
スクロールしたら吹き出しが現れ、一文字ずつアニメーションさせるコードを紹介しました。全体はかなり長いので、 以下に、リンクを貼っておきます。