整活代码整理分享
🌸
有几个效果的代码我整理到一个html文件中了,可直接保存为HTML文件运行。其中关于数据需要自行修改(例如下落的文字、相册),代码中使用的插件我放在我网站目录下了,嫌慢可自己放入cdn中,其他需求自行修改,也可以找我一起研究🐸暂时整理这几个,后面有需要了我再继续。
另外有没有大佬知道,为什么我的代码块渲染不出来呀!!现在这个效果很丑
文字坠落效果
先分享一个插件BRM·IO,Github连接,很牛逼,用法太多了,可以自己去看一下的是使用范例。(纯英文对我不是很友好😥,对这个感兴趣的话网上有很多教程)
Matter是一个开源的JavaScript物理引擎库,可以帮助开发者实现基于物理引擎的动画和交互效果。Matter提供了丰富的API,各种物理参数可以轻松配置,例如重力、摩擦力、弹簧等。开发者可以使用Matter实现物理运动、碰撞检测、拖拽等效果,帮助开发者节省大量时间和精力。Matter适用于在Web浏览器中创建交互式的游戏、模拟和动画。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入matter插件,需要的话自行下载引入 -->
<script src="https://yaoyuan.vip/js/Plugins/matter.js"></script>
<style>
body { margin: 0; padding: 0; background: transparent; font-family: 'fantasy','Inconsolata', monospace; width: 100vw; height: 100vh; overflow: hidden;}
* { user-select: none; }
.word { position: absolute; cursor: grab; font-size: 25px; color: black; }
.word.highlighted { font-weight: bold; color: #69C1AE;}
#abstract { color: #69C1AE; font-size: 30px; position: absolute; width: 100vw; height: 100vh; display: flex;align-items: center;justify-content: center;align-items: center;}
</style>
</head>
<body>
<!-- 默认展示,可通过类名调节样式 -->
<div id="abstract">姚远,软件工程,前端开发(就这些?)</div>
<!-- 会通过空格对字符进行分割 -->
<div id="text" style="display: none;"> 🐋 🐬 🐳 🐟 我叫姚远,🐋 🐬 🐳 🐟 姚远, 姚远 , 。 🐋 🐬 🐳 🐟 * @ ~ 🐋 🐬 🐳 🐟 00后 00年 计算机 , 🐋 🐬 🐳 🐟 软件工程专业, 西安 , 前端开发 , 目前工作内容就是, 每天摸鱼写博客 , 干啥都很菜 脾气也不好 工作很摆烂 懒滴 , 也没啥写的, 主要是想整一些活, 打发打发时间 罢liao</div>
</body>
<script>
const textNode = document.querySelector("#text");
const abstract = document.querySelector("#abstract");
const splitWords = () => {
const text = textNode.textContent;
const newDomElements = text.split(" ").map((text) => {
// 被分割的字符如果开头是这三个字,那么会高亮显示
const highlighted = text.startsWith(`"前"`) || text.startsWith(`软`) || text.startsWith(`姚`);
return `<span class="word ${ highlighted ? "highlighted" : null }">${ text }</span>`;
});
textNode.innerHTML = newDomElements.join("");
};
const renderCanvas = () => {
const Engine = Matter.Engine;
const Render = Matter.Render;
const World = Matter.World;
const Bodies = Matter.Bodies;
const Runner = Matter.Runner;
const params = { isStatic: true, render: { fillStyle: "transparent" }
};
const canvasSize = { width: window.innerWidth, height: window.innerHeight };
const engine = Engine.create({});
const render = Render.create({ element: document.body, engine: engine, options: { ...canvasSize, background: "transparent", wireframes: false } });
const floor = Bodies.rectangle( canvasSize.width / 2, canvasSize.height, canvasSize.width, 50, params );
const wall1 = Bodies.rectangle( 0, canvasSize.height / 2, 50, canvasSize.height, params );
const wall2 = Bodies.rectangle( canvasSize.width, canvasSize.height / 2, 50, canvasSize.height, params );
const top = Bodies.rectangle( canvasSize.width / 2, 0, canvasSize.width, 50, params );
const wordElements = document.querySelectorAll(".word");
const wordBodies = [...wordElements].map((elemRef) => {
const width = elemRef.offsetWidth;
const height = elemRef.offsetHeight;
return {
body: Matter.Bodies.rectangle(canvasSize.width / 2, 0, width, height, { render: { fillStyle: "transparent" } }),
elem: elemRef,
render() {
const { x, y } = this.body.position;
this.elem.style.top = `${y - 20}px`;
this.elem.style.left = `${x - width / 2}px`;
this.elem.style.transform = `rotate(${this.body.angle}rad)`;
}
};
});
const mouse = Matter.Mouse.create(document.body);
const mouseConstraint = Matter.MouseConstraint.create(engine, { mouse, constraint: { stiffness: 0.2, render: { visible: false}}});
mouse.element.removeEventListener("mousewheel", mouse.mousewheel);
mouse.element.removeEventListener("DOMMouseScroll", mouse.mousewheel);
World.add(engine.world, [ floor, ...wordBodies.map((box) => box.body), wall1, wall2, top, mouseConstraint ]);
render.mouse = mouse;
Runner.run(engine);
Render.run(render);
(function rerender() {
wordBodies.forEach((element) => { element.render(); });
Matter.Engine.update(engine);
requestAnimationFrame(rerender);
})();
};
abstract.addEventListener("mouseover", (event) => {
// 鼠标移入区域隐藏默认、显示文字、切割字符、绘制画布
abstract.style.display = 'none'
textNode.style.display = 'block'
splitWords();
renderCanvas();
});
</script>
</html>
鼠标滑过脚印效果
- 在页面中插入脚印元素
<svg id="footstep" viewBox="0 0 0 0" style="position: fixed;top: 0;left: 0;width: 100vw;height: 100vh;pointer-events:none">
<defs>
<path id="feet-shape"
d="M41.5,30.2C36,24.6,4.7,26.1,7.7,49.4c.8,5.9,4,10.2,8,19.9,3,7.2-.1,15.7,5.8,20.8S43,91.6,38.6,75.9c-1.8-6.5-7.6-9.3-8.9-14.1C26.1,47.9,51.7,40.6,41.5,30.2Z M41.7,7.6c-2.6-.3-5.2,2.8-5.6,7s1.3,7.8,3.9,8.1,5.2-2.9,5.6-7.1S44.4,7.8,41.7,7.6Z M28.8,21.9c2.2.2,4.1-2.1,4.5-5.1s-1.1-5.7-3.2-6-4.1,2.1-4.5,5.1S26.7,21.6,28.8,21.9Z M20.1,23.3c1.6.1,3.1-1.8,3.4-4.4s-.8-4.7-2.4-4.9-3,1.8-3.3,4.3S18.5,23.1,20.1,23.3Z M14.9,25.5c1.4-.2,2.4-1.9,2.1-3.9s-1.6-3.4-3-3.3-2.4,2-2.2,3.9S13.4,25.7,14.9,25.5Z M10.9,29.2c1-.1,1.7-1.4,1.5-2.8s-1.1-2.5-2.2-2.4-1.7,1.4-1.6,2.8S9.8,29.3,10.9,29.2Z"/>
<symbol id="feet-left" viewBox="0 0 100 100">
<rect x="0" y="0" width="100" height="100" fill="none"/>
<use xlink:href="#feet-shape"/>
</symbol>
<symbol id="feet-right" viewBox="0 0 100 100">
<rect x="0" y="0" width="100" height="100" fill="none"/>
<g transform="scale(-1, 1) translate(-100, 0)">
<use xlink:href="#feet-shape"/>
</g>
</symbol>
</defs>
</svg>
- 添加执行的Js代码
const svgEl = document.querySelector('#footstep');
let feetEls = [];
const pointer = {
x: 0,
y: 0,
dx: 0,
dy: 0,
angle: 0,
moving: false,
justStopped: false,
}
const stepsNumber = 9;
const iconSize = 50;
const mouseRepel = 35;
const feetPositions = [];
updateLayout();
window.addEventListener('resize', updateLayout);
createFeet();
let stepsCnt = 0;
let accumDx = 0;
let accumDy = 0;
let accumDist = 0;
let introAnimationIsPlaying = false;
render();
introAnimation();
window.addEventListener("mousemove", (e) => {
if (!introAnimationIsPlaying) {
onPointerMove(e.clientX, e.clientY)
}
});
window.addEventListener("touchmove", (e) => {
if (!introAnimationIsPlaying) {
onPointerMove(e.targetTouches[0].clientX, e.targetTouches[0].clientY)
}
});
function updateLayout() {
svgEl.setAttribute("viewBox", "0 0 " + window.innerWidth + " " + window.innerHeight)
}
function createFeet() {
for (let i = 0; i < stepsNumber; i++) {
const el = document.createElementNS("http://www.w3.org/2000/svg", 'use');
el.setAttribute("href", i % 2 ? "#feet-left" : "#feet-right");
el.setAttribute("x", "-" + (.5 * iconSize));
el.setAttribute("y", "-" + (.5 * iconSize));
el.setAttribute("width", "" + iconSize);
el.setAttribute("height", "" + iconSize);
svgEl.appendChild(el);
feetPositions.push({x: 0, y: 0, angle: 0, age: 0})
feetEls.push(el);
gsap.set(el, {
opacity: 0,
transformOrigin: "center center",
})
}
}
function onPointerMove(x, y) {
pointer.dx = x - pointer.x;
pointer.dy = y - pointer.y;
pointer.x = x;
pointer.y = y;
pointer.moving = true;
accumDx += pointer.dx;
accumDy += pointer.dy;
pointer.angle = Math.atan2(pointer.dx, pointer.dy);
accumDist = Math.sqrt(Math.pow(accumDx, 2) + Math.pow(accumDy, 2));
if (accumDist > 70) {
stepsCnt++;
accumDx = 0;
accumDy = 0;
accumDist = 0;
feetPositions.unshift({
x: pointer.x,
y: pointer.y,
angle: (1 - pointer.angle / Math.PI) * 180,
age: 1
});
feetPositions.length = stepsNumber;
feetPositions[0].x -= Math.sin(pointer.angle) * mouseRepel;
feetPositions[0].y -= Math.cos(pointer.angle) * mouseRepel;
for (let fIdx = 1; fIdx < stepsNumber; fIdx++) {
updateFootEl(feetEls[fIdx], fIdx, (fIdx % 2 === stepsCnt % 2));
}
gsap.set(feetEls[0], {
opacity: 0
})
}
}
function render() {
for (let fIdx = 1; fIdx < (stepsNumber); fIdx++) {
feetPositions[fIdx].age -= (pointer.moving ? .05 : .1);
}
for (let fIdx = 2; fIdx < (stepsNumber); fIdx++) {
gsap.set(feetEls[fIdx], {
opacity: feetPositions[fIdx].age
})
}
if (pointer.moving) {
pointer.moving = false;
pointer.justStopped = true;
} else if (pointer.justStopped) {
pointer.justStopped = false;
// 当鼠标停止移动时执行一次
updateFootEl(feetEls[0], 0, (0 === stepsCnt % 2));
gsap.set(feetEls[0], {
opacity: 1
})
updateFootEl(feetEls[1], 0, (1 === stepsCnt % 2), .1);
gsap.set(feetEls[1], {
delay: .1,
opacity: 1
})
for (let fIdx = 2; fIdx < (stepsNumber); fIdx++) {
updateFootEl(feetEls[fIdx], fIdx - 1, ((fIdx - 1) % 2 === stepsCnt % 2));
}
}
requestAnimationFrame(render);
}
function updateFootEl(el, posIdx, isLeft, delay = 0) {
gsap.set(el, {
delay: delay,
x: feetPositions[posIdx].x,
y: feetPositions[posIdx].y,
rotation: feetPositions[posIdx].angle,
attr: {
href: isLeft ? "#feet-left" : "#feet-right"
},
})
}
function introAnimation() {
introAnimationIsPlaying = true;
const mouseCoords = {x: -100, y: window.innerHeight}
gsap.timeline({
onUpdate: () => {
onPointerMove(mouseCoords.x, mouseCoords.y);
},
onComplete: () => {
introAnimationIsPlaying = false;
}
})
.to(mouseCoords, {
x: .4 * window.innerWidth,
ease: "power1.out"
})
.to(mouseCoords, {
y: .6 * window.innerHeight,
ease: "back.out(3)"
}, 0);
}
跟屁虫小狗
- 在页面中插入小狗元素
<div class="wrapper">
<!-- 小狗的位置 -->
<div class="marker red d-none"></div>
<!-- 鼠标的位置 -->
<div class="marker green d-none"></div>
<!-- 狗狗面朝位置-->
<div class="marker blue d-none"></div>
<div class="dog">
<div class="body-wrapper">
<div class="body img-bg"></div>
</div>
<div class="head-wrapper">
<div class="head img-bg"></div>
</div>
<div class="leg-wrapper">
<div class="leg one img-bg"></div>
</div>
<div class="leg-wrapper">
<div class="leg two img-bg"></div>
</div>
<div class="leg-wrapper">
<div class="leg three img-bg"></div>
</div>
<div class="leg-wrapper">
<div class="leg four img-bg"></div>
</div>
<div class="tail-wrapper">
<div class="tail img-bg"></div>
</div>
</div>
</div>
- 小狗样式
//修狗
.wrapper {
position: absolute;
width: 100%;
height: 100%;
display: flex;
// justify-content: center;
// align-items: center;
z-index: -1;
// pointer-events:;
}
.wrapper .leg {
position: absolute;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAMCAYAAABfnvydAAAAAXNSR0IArs4c6QAAAElJREFUKFNjZICC/////4exQTQjIyMjmAYR6JIwhSBFjLMiLP6nLj+OrBnOnh1pyTBSFIA8jS0sQGGQtuIEJDhhipBDCyQJ4gMALug8VaRjkWwAAAAASUVORK5CYII=);
width: calc(2 * 8px);
height: calc(2 * 12px);
background-size: calc(2 * 8px) calc(2 * 12px) !important;
transition: 0.15s;
}
.wrapper .body {
position: absolute;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASAAAAAwCAYAAACxIqevAAAAAXNSR0IArs4c6QAABSpJREFUeF7tm32a1CAMxqe30pvoAfRQegC9id5qfPBZlGELJOQL2nf/2t0pkPySvA20czzwAwIgAAJBBI6gdbEsCIAACDwgQEgCEACBMAIQoDD0WBgEQAAChBwAARB4fPv04UnF8PXnbzXdUJuIajyuAwEQWINAKTpffvwiG/X988d/10rFCAJExo4LQeA6BJL4cESn5XkSI4kIQYCuk1PwBARIBLTEJy8mESEIEClkuAgErkFAW3ykIgQBukZewQsQIBGAAJEw4SIQAAFtAlbiI+mC0AFpRxnzgcCiBCBAiwYGZvkRGL1vInmi4ufFnitBgPaMG6xWIJCFZ/ToN79jAiFSgF5N8Xw+yS8bzq5+HAdrV8W6eNYojLsvAarw1IQ0X3a7L/1XzyFAyIRbEdBq+dEVydNGKxYjS7jvBKEDGhHF59MEtO+4EKLpUPz9rtdo+zs/+/+RECANiphDTEBbfEqDIoQoFfDZudTZ/3vXZj/quVpjxIF4m+DyAjR6ulGDXPWQkerHqvZrJaxknlWTXepTGl/GPedK/b/0dy0oPYHJn1mK0KoxEW3BZr9NmwK5wiGjxP6czBF3Y0kheYz1SvacRx43g1okzkRjJDo9wcpxsRIhr5i4bcE0HeIaLS2i2SczvXU9faB2aT17LYvWcvt15pMH+1IYer9TOv16PGWMJOc1a5ViByceUx2QhUMcoykQWtdY2F52QxaFbSGYNR/u+xsRfCNvAL1znRzzUQd01uVQx6ya89IbAluAdizgMvDWTwI0hdRDeLSFyDI/RkWoyb5eSypAZdd6dmZkuQXzjgknDrcRIK8gcOD3CsrL3jMbJN1QpN3JF4ntI4Hb9XPvmHBq4BYC5BkADvzVtjHZHkkRe7Ju8ZPYv6vIrHQz49QAS4A8kotjPCVZPGwu7ZDa722vdhGvYL80BmdMemc1nEPl1lYsrdna5lHyHAIkpfQ2XjN5IopBan+EzZrbsBXsl8ZAIkC9s5zRgTMEaCAiHsmllTwetkqfALQS3fqgnHKvmN3GRHHX7EIhQJQMaV/DqWHyFswzsTgOtDB4v4uS7NCw25Nzi53Ujwj2HgJU82o9fm9tp9ABvc84CJBM7F9GSws3J250ByT1I1pEpfZHdUCKqfhuKq+YcNlfUoC8YFvcdSNsr7OVm0QrbiVnt5AtEaAeQvfOgPINJl/j+YVUr7zi5g4ESOm2wwXfS/TdO6CyCCN80YqFUmosMQ0EiBEGaQJ5wc4uSe0t0XjbfhaWnf3RtJ2Rsltcap1bM+zRASmkzgz43rLWiTJy2cKftKZHN6Rt+4jVbp/n95A0Y5GYp5+Z70FCgIQZZJHwkQJk4Y/HlkxSBMIU2HK4hhBpMIcACdNH+7CzLFavrsFiK9k749L0S6MIhCmw9fAZIdJkDgESpI9lt+ApRJoJRcVZfiVhZjsQYTPVtx2vK+Mxsn9mq9WaEwI0ot343EN8yqWlBVu7kQt4du8+ia07jFoEmgVg4QfmpBMgC1Ca0uNsQquwLd/G1bKRHqbXK6mF2pofBTxLHuO0CVxWgKzEMlp8tBMA84FAJIHLCpBFxwbxiUxVrH1FAiwBsijqEqpFgc+c8rfOS7B1uWIJwKdIApcXoAx35hAXT1oiUxNr34EAW4CsuiCL7qcVQOohLjqeO5QAfIwkMCVA2iLkKT6RsLE2CIDAK4FpAcoilKfjvky24nsoSA4QAAFfAiIBKk2lbmvyGGxvfAON1UBgRQJqArSic7AJBEBgbQJ/AOoMmm0ZeBaqAAAAAElFTkSuQmCC);
width: calc(2 * 6 * 48px);
height: calc(2 * 48px);
background-size: calc(2 * 6 * 48px) calc(2 * 48px) !important;
}
.wrapper .dog {
position: absolute;
width: calc(2 * 48px);
height: calc(2 * 48px);
animation: fade-in forwards 1s;
transition: 0.5s;
z-index: 9999;
bottom: 50px;
left: 50px;
}
@keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
.wrapper .head {
position: absolute;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALoAAAAgCAYAAACl82LUAAAAAXNSR0IArs4c6QAAA+hJREFUeF7tmtuV1DAMhiddQSdQABS1FACdQFezx4cVR2h1+X1LnJXnaSbjRLfPkhz7eOzP9kACDxwJbNwmbg88bgP6y5dPz++//txG383WWh64BTgF8m8/fz9+fP382LCvBdBdtNmgN0Rqteqymj4NLp1+CwR6rSNrx3tWUjanMVdn9dWqy2r69BJbyw46PgRdOrL8towpbcVox3N5pX0pnwK7/F6u0Wdke0PyqXWydJghuzzT8rdm/0i7e4FtuX8ma1Wgk3M9IwjCEZlXZnMpl8viuhH0FHiCpQYEDjivJlKO9ImU3Rrwcl/kbz7hKQHU2Nii28x7OOiR7TzhIay5oGttg6fAKMdrkGkOtkC3xiIQeLI1+yx/IM6XeqJ2WxOPrh/HESawmcC2PHs2a6pDrJklA21lWBmImqBHWbwmu2r6ebCjstFAjrA78rmlSyTb6m21695Yq2VDe+ezWHsH+uhgczCjjFojuya7IjrUyEZBR9sJS3ZNxaqtYlo7Z12jtRePnwcy/RfBfqbP/wN9luCzQdMWqx50V9pd9JoFemQzB1iDU4Kq/S4yvAngVQOkD69JKnysrGgfHnQKtgwId8qVoEeQR/14BILVwnAAve/y+VpVlvej91wC+uxge1k9q2wkm7eCLquaBBTp0aOMTrpZE4Xs02TPhFxj7V9Gzwrbqna3LkK1iaFl9V7QqZ9vaV2u8PkG/e0cTdQCRP/T+3MvU8nXfl7Ae0H3+tXIltn/b9Ane9jKbCPKqAe6tWt7RcAnuxh6vLcuKQ+oiUfkd2qbUmV0bSPlTNi0Ml8TVIQiHnhvl9rrv2sWl1YLY/Xn5frz+VSPkSBVUfoguodingZ0bwNlFuzRTnGrXCu4vHLwszDRmxIJpda/I9fQxasFujWRNbuQSV/GvAPdm2noQ5Fx1vZ0a9ARmWXMFaCTXNJRA6426PyZ3vGDDfpfry+V0SmjjC7jfBJE5z9mTzTudK7XLLmUBa3daN5yyImIZG+tNUEzemSzthBHr3Hf8uS2zIbRTNijcx/knCgAaPXQxnkTrVeutRNsTa4I0lGge/6qeeNkHYXw3ky5O6MfDbYoq2mBoEw3qrqgOvTKlX17NLnRxShPAlavb7Vm1rt6JLFYfTnSr2u2m6cXizIjgo0GWpbzHvktMiX0PeBZrxORitAjl/fv0QE6RJczxvTaK1uV8luzPTyP3mtsj8PJCXT4CNWlR6aX5RH5I2VrfbSlw0i5iJ2jx1ivKb21BHUgVkXhOt7ugP5oB+/n5fDABj1HnNNbuUFPj0AOB2zQc8Q5vZUb9PQI5HDABj1HnNNbuUFPj0AOB7wCKr30XX1N2MUAAAAASUVORK5CYII=);
width: calc(2 * 6 * 31px);
height: calc(2 * 32px);
background-size: calc(2 * 6 * 31px) calc(2 * 32px) !important;
}
.wrapper .head-wrapper.happy > .head {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALoAAAAgCAYAAACl82LUAAAAAXNSR0IArs4c6QAABCpJREFUeF7tmtlx3DAMQFddxZ0kBSS/Hlfi8W9cQNJJ0tV6qAl2YAgnRVBSyP2yJYq4HkHwWG7zNz0wgAeWAWycJk4P3C4D+s+vX+4/fv+9jL6TrXN54BLgFMi///pze//2dJuwnwugq2gzQa+I1Nlml7PpU+HS9E9coEcdGW2vWQnZHNocndXPNrucTZ+9xEbZ8bY3QaeOLP9LxpSyorXjsbxSvpRfgZ3+XZ7Br2V5A/KhdJJ0yJBd+pT8zdnf0u69wNZ8n8laCHRwrmYEQNgi89JsTuViWVg3gB4CD7BEQMCA49mEyqE+obJrA16+s/yNBzwkgIiNNbplfoNBt2zHCc/Dmgo6VzZoCrRyPAcZ52AJdKmtBwJNNmef5A+P86meXrulgQfPl2UxE1gmsDV9Z7PGOkQaWTTQUoalgYgE3crikezK6afB7pXtDWQLuy2fS7pYsqXalnuutZVKNm/t3Iu1Deitg43BtDJqRHYku3p0iMj2gu4tJyTZkRkrOotx5Zz0DNZeOH4ayPDOgr2nzz+BniW4N2jcYlWD7ki7i15ZoFs2Y4A5OCmo3P9FhjYAtNnAU4dHkgpuS2e0/x50CPa6uHt+vb2/vWwOnY4E3YLcqsctEKQSBgOo/U3752Zl+r33m0NAzw62ltV7yF6z/PPrqgaGvYdsK7NyAa8pzWhGw1uQFFBPjW5ldJAnDRSYrTjZmZBzrD0y+pEB7ymbm2p7Ox0Dou3a7NGLlm+tQYd6vqZ06RlvsHs40LOyC+yfa3DSbT8t4LW7LdFFqVX6ZLyfoGd4FfXJ1autnK6BLp3atpJtuc3aarS+b/1eW5esa6l/J+AeuZbfh8zo3EFKT9h6lE048NoptVZ/RxaXUgkj1efl+f1+Z6+ReGZFCr/1DcR8mNJFy2pZsFsnxbVypeDimUNbiHIQagtKbVFZs7UogS5lcM4uT7YvbTagayPN26mnnXQ8XRt0j0xt1wOCGJkuvTJBLm5P1wjRoOM+PQtZz/YiBZnbkfE88+7SRG1uCno2aBBszfFZsOGRLUGaab+0tZk1yHDG1xIL9QW+BOfZpbHA1rYwI3emYHBzF+isfjY1epbTsTOtRVEWbJZc0DFL/iNQSQdWm5Ng5ryAQu2t0bFvLPglsLnkEtlxgkRRzj+s26NSQj3VXZfWgw0ym3XHBgcCFletZhesgwbCXrmfpvdOoEO8wH/WQOAGm1Z+QXs46FsTxtvLo5vIzVHx9mLprUWwj4CtRiYXhFofSNuJntp+L/C4ho8McI9uGW0se3HZB6Bbg4Oz27yPvte4Pc4GJ8DlI68ue2RK0+wRsvHWnSW/tc2WvNbvrW1KbdaQ3mEdL3dBv7WDZ39jeGCCPkach7dygj48AmM4YII+RpyHt3KCPjwCYzhggj5GnIe3coI+PAJjOOADdCQTbGUe9fwAAAAASUVORK5CYII=);
}
.wrapper .head-wrapper.happy {
animation: infinite 0.5s pant;
}
@keyframes pant {
0%, 100% { transform: translateY(-1px); }
50% { transform: translateY(1px); }
}
.wrapper .head-wrapper.flip.happy {
animation: infinite 0.5s pant-flip;
}
@keyframes pant-flip {
0%, 100% { transform: translateY(-1px) scale(-1, 1); }
50% { transform: translateY(1px) scale(-1, 1); }
}
.wrapper .tail {
position: absolute;
background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAADZJREFUKFNjZICCWREW/2FsEJ224gQjiAYTIMnU5ceR5RlmR1qCFTFik4SpBCmihwKCjiTkTQB1sCqti9mJ/QAAAABJRU5ErkJggg==);
width: calc(2 * 8px);
height: calc(2 * 8px);
background-size: calc(2 * 8px) !important;
}
.wrapper .tail-wrapper {
position: absolute;
width: calc(2 * 8px);
height: calc(2 * 8px);
transition: 0.15s;
}
.wrapper .body-wrapper {
position: absolute;
width: calc(2 * 48px);
height: calc(2 * 48px);
overflow: hidden;
}
.wrapper .body-wrapper,
.head-wrapper {
z-index: 1;
}
.wrapper .walk-1 {
animation: infinite 0.4s walking;
animation-delay: 0;
}
.wrapper .walk-2 {
animation: infinite 0.4s walking;
animation-delay: 0.2s;
}
@keyframes walking {
0%, 100% { transform: translateY(-4px); }
50% { transform: translateY(0); }
}
.wrapper .wag {
animation: infinite 0.5s wag;
}
@keyframes wag {
0%, 100% { transform: translateX(-2px); }
50% { transform: translateX(2px); }
}
.wrapper .head-wrapper {
position: absolute;
top: 6px;
left: 16px;
width: calc(2 * 31px);
height: calc(2 * 32px);
overflow: hidden;
}
.wrapper .flip {
transform: scale(-1, 1);
}
.wrapper .img-bg {
image-rendering: pixelated;
background-repeat: no-repeat !important;
}
.wrapper .indicator {
position: fixed;
top: 10px;
left: 10px;
color: #9a5838;
}
.wrapper .d-none {
display: none;
}
.wrapper .indicator {
position: fixed;
top: 10px;
right: 10px;
}
.wrapper .marker {
width: 10px;
height: 10px;
border-radius: 50%;
position: absolute;
transition: 0.5s;
z-index: 100;
margin-top: -5px;
margin-left: -5px;
}
.wrapper .red { background-color: rgb(255, 64, 0); }
.wrapper .green { background-color: rgb(42, 239, 190); }
.wrapper .blue { background-color: rgb(0, 140, 255); }
- 小狗交互
function init() {
const elements = {
body: document.body,
wrapper: document.querySelector('.wrapper'),
dog: document.querySelector('.dog'),
marker: document.querySelectorAll('.marker'),
}
const animationFrames = {
rotate: [[0], [1], [2], [3], [5], [3, 'f'], [2, 'f'], [1, 'f']]
}
const directionConversions = {
360: 'up',
45: 'upright',
90: 'right',
135: 'downright',
180: 'down',
225: 'downleft',
270: 'left',
315: 'upleft',
}
const angles = [360, 45, 90, 135, 180, 225, 270, 315]
const defaultEnd = 4
// A ---- A ________ ________
// | | | |
// | ^ ^ | | |
// ____^___ _________|________|
// | | | | | | | |
// 1 2 3 4
// L R L R
const partPositions = [
{ //0
leg1: { x: 26, y: 43 },
leg2: { x: 54, y: 43 },
leg3: { x: 26, y: 75 },
leg4: { x: 54, y: 75 },
tail: { x: 40, y: 70, z: 1 },
},
{ //1
leg1: { x: 33, y: 56 },
leg2: { x: 55, y: 56 },
leg3: { x: 12, y: 72 },
leg4: { x: 32, y: 74 },
tail: { x: 20, y: 64, z: 1 },
},
{ //2
leg1: { x: 59, y: 62 },
leg2: { x: 44, y: 60 },
leg3: { x: 25, y: 64 },
leg4: { x: 11, y: 61 },
tail: { x: 4, y: 44, z: 1 },
},
{ //3
leg1: { x: 39, y: 63 },
leg2: { x: 60, y: 56 },
leg3: { x: 12, y: 52 },
leg4: { x: 28, y: 50 },
tail: { x: 7, y: 21, z: 0 },
},
{ //4
leg1: { x: 23, y: 54 },
leg2: { x: 56, y: 54 },
leg3: { x: 24, y: 25 },
leg4: { x: 54, y: 25 },
tail: { x: 38, y: 2, z: 0 },
},
{ //5
leg1: { x: 21, y: 58 },
leg2: { x: 41, y: 64 },
leg3: { x: 53, y: 50 },
leg4: { x: 69, y: 53 },
tail: { x: 72, y: 22, z: 0 },
},
{ //6
leg1: { x: 22, y: 59 },
leg2: { x: 30, y: 64 },
leg3: { x: 56, y: 60 },
leg4: { x: 68, y: 62 },
tail: { x: 78, y: 40, z: 0 },
},
{ //7
leg1: { x: 47, y: 45 },
leg2: { x: 24, y: 53 },
leg3: { x: 68, y: 68 },
leg4: { x: 47, y: 73 },
tail: { x: 65, y: 65, z: 1 },
},
]
const control = {
x: null,
y: null,
angle: null,
}
const distance = 30
const nearestN = (x, n) => x === 0 ? 0 : (x - 1) + Math.abs(((x - 1) % n) - n)
const px = num => `${num}px`
const radToDeg = rad => Math.round(rad * (180 / Math.PI))
const degToRad = deg => deg / (180 / Math.PI)
const overlap = (a, b) =>{
const buffer = 20
return Math.abs(a - b) < buffer
}
const rotateCoord = ({ angle, origin, x, y }) =>{
const a = degToRad(angle)
const aX = x - origin.x
const aY = y - origin.y
return {
x: (aX * Math.cos(a)) - (aY * Math.sin(a)) + origin.x,
y: (aX * Math.sin(a)) + (aY * Math.cos(a)) + origin.y,
}
}
const setStyles = ({ target, h, w, x, y }) =>{
if (h) target.style.height = h
if (w) target.style.width = w
target.style.transform = `translate(${x || 0}, ${y || 0})`
}
const targetAngle = dog =>{
if (!dog) return
const angle = radToDeg(Math.atan2(dog.pos.y - control.y, dog.pos.x - control.x)) - 90
const adjustedAngle = angle < 0 ? angle + 360 : angle
return nearestN(adjustedAngle, 45)
}
const reachedTheGoalYeah = (x, y) =>{
return overlap(control.x , x) && overlap(control.y, y)
}
const positionLegs = (dog, frame) => {
;[5, 7, 9, 11].forEach((n, i) => {
const { x, y } = partPositions[frame][`leg${i + 1}`]
setStyles({
target: dog.childNodes[n],
x: px(x), y: px(y),
})
})
}
const moveLegs = dog => {
;[5, 11].forEach(i => dog.childNodes[i].childNodes[1].classList.add('walk-1'))
;[7, 9].forEach(i => dog.childNodes[i].childNodes[1].classList.add('walk-2'))
}
const stopLegs = dog => {
;[5, 11].forEach(i => dog.childNodes[i].childNodes[1].classList.remove('walk-1'))
;[7, 9].forEach(i => dog.childNodes[i].childNodes[1].classList.remove('walk-2'))
}
const positionTail = (dog, frame) => {
setStyles({
target: dog.childNodes[13],
x: px(partPositions[frame].tail.x), y: px(partPositions[frame].tail.y),
})
dog.childNodes[13].style.zIndex = partPositions[frame].tail.z
dog.childNodes[13].childNodes[1].classList.add('wag')
}
const animateDog = ({ target, frameW, currentFrame, end, data, part, speed, direction }) => {
const offset = direction === 'clockwise' ? 1 : -1
target.style.transform = `translateX(${px(data.animation[currentFrame][0] * -frameW)})`
if (part === 'body') {
positionLegs(data.dog, currentFrame)
moveLegs(data.dog)
positionTail(data.dog, currentFrame)
} else {
target.parentNode.classList.add('happy')
}
data.angle = angles[currentFrame]
data.index = currentFrame
target.parentNode.classList[data.animation[currentFrame][1] === 'f' ? 'add' : 'remove']('flip')
let nextFrame = currentFrame + offset
nextFrame = nextFrame === -1
? data.animation.length - 1
: nextFrame === data.animation.length
? 0
: nextFrame
if (currentFrame !== end) {
data.timer[part] = setTimeout(()=> animateDog({
target, data, part, frameW,
currentFrame: nextFrame, end, direction,
speed,
}), speed || 150)
} else if (part === 'body') {
// 结束
control.angle = angles[end]
data.walk = true
setTimeout(()=> {
stopLegs(data.dog)
}, 200)
setTimeout(()=> {
document.querySelector('.happy')?.classList.remove('happy')
}, 5000)
}
}
const triggerDogAnimation = ({ target, frameW, start, end, data, speed, part, direction }) => {
clearTimeout(data.timer[part])
data.timer[part] = setTimeout(()=> animateDog({
target, data, part, frameW,
currentFrame: start, end, direction,
speed,
}), speed || 150)
}
const getDirection = ({ pos, facing, target }) =>{
const dx2 = facing.x - pos.x
const dy1 = pos.y - target.y
const dx1 = target.x - pos.x
const dy2 = pos.y - facing.y
return dx2 * dy1 > dx1 * dy2 ? 'anit-clockwise' : 'clockwise'
}
const turnDog = ({ dog, start, end, direction }) => {
triggerDogAnimation({
target: dog.dog.childNodes[3].childNodes[1],
frameW: 31 * 2,
start, end,
data: dog,
speed: 100,
direction,
part: 'head'
})
setTimeout(()=>{
triggerDogAnimation({
target: dog.dog.childNodes[1].childNodes[1],
frameW: 48 * 2,
start, end,
data: dog,
speed: 100,
direction,
part: 'body'
})
}, 200)
}
const createDog = () => {
const { dog } = elements
const { width, height, left, top } = dog.getBoundingClientRect()
dog.style.left = px(left)
dog.style.top = px(top)
positionLegs(dog, 0)
const index = 0
const dogData = {
timer: {
head: null, body: null, all: null,
},
pos: {
x: left + (width / 2),
y: top + (height / 2),
},
actualPos: { x: left, y: top },
facing: {
x: left + (width / 2),
y: top + (height / 2) + 30,
},
animation: animationFrames.rotate,
angle: 360,
index,
dog,
}
elements.dog = dogData
turnDog({
dog: dogData,
start: index, end: defaultEnd,
direction: 'clockwise'
})
positionTail(dog, 0)
}
const checkBoundaryAndUpdateDogPos = (x, y, dog, dogData) =>{
const lowerLimit = -40 // 离边缘距离
const upperLimit = 40
if (x > lowerLimit && x < (elements.body.clientWidth - upperLimit)){
dogData.pos.x = x + 48
dogData.actualPos.x = x
}
if (y > lowerLimit && y < (elements.body.clientHeight - upperLimit)){
dogData.pos.y = y + 48
dogData.actualPos.y = y
}
dog.style.left = px(x)
dog.style.top = px(y)
}
const positionMarker = (i, pos) => {
if(elements.marker[i]){
elements.marker[i].style.left = px(pos.x)
elements.marker[i].style.top = px(pos.y)
}
}
const moveDog = () =>{
clearInterval(elements.dog.timer.all)
const { dog } = elements.dog
elements.dog.timer.all = setInterval(()=> {
const { left, top } = dog.getBoundingClientRect()
const start = angles.indexOf(elements.dog.angle)
const end = angles.indexOf(targetAngle(elements.dog))
// 停止
if (reachedTheGoalYeah(left + 48, top + 48)) {
clearInterval(elements.dog.timer.all)
const { x, y } = elements.dog.actualPos
dog.style.left = px(x)
dog.style.top = px(y)
stopLegs(dog)
turnDog({
dog: elements.dog,
start,
end: defaultEnd,
direction: 'clockwise'
})
return
}
let { x, y } = elements.dog.actualPos
const dir = directionConversions[targetAngle(elements.dog)]
if (dir !== 'up' && dir !== 'down') x += (dir.includes('left')) ? -distance : distance
if (dir !== 'left' && dir !== 'right') y += (dir.includes('up')) ? -distance : distance
positionMarker(0, elements.dog.pos)
positionMarker(1, control)
const { x: x2, y: y2 } = rotateCoord({
angle: elements.dog.angle,
origin: elements.dog.pos,
x: elements.dog.pos.x,
y: elements.dog.pos.y - 100,
})
elements.dog.facing.x = x2
elements.dog.facing.y = y2
positionMarker(2, elements.dog.facing)
if (start === end) {
elements.dog.turning = false
}
if (!elements.dog.turning && elements.dog.walk) {
if (start !== end) {
elements.dog.turning = true
const direction = getDirection({
pos: elements.dog.pos,
facing: elements.dog.facing,
target: control,
})
turnDog({
dog: elements.dog,
start, end, direction,
})
} else {
checkBoundaryAndUpdateDogPos(x, y, dog, elements.dog)
moveLegs(dog)
}
}
}, 200)
}
createDog()
const triggerTurnDog = () => {
const dog = elements.dog
dog.walk = false
control.angle = null
const direction = getDirection({
pos: dog.pos,
facing: dog.facing,
target: control,
})
const start = angles.indexOf(dog.angle)
const end = angles.indexOf(targetAngle(dog))
turnDog({
dog,
start, end, direction
})
}
elements.body.addEventListener('mousemove', e =>{
control.x = e.pageX
control.y = e.pageY
triggerTurnDog()
})
elements.body.addEventListener('click', moveDog)
}
window.addEventListener('DOMContentLoaded', init)
人来人往
这个是我直接从别人那里抄过来的,逛博客看到这个设计真的驻足了好久,很喜欢。后来发现我逛的那位博主不是原创,我记得我在ins还是Youtube上看到原创了,但是没点关注找不到了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>people</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,minimal-ui">
<style>
body, html { height: 100%; }
body { margin: 0 }
#canvas { width: 100%; height: 100% }
body::-webkit-scrollbar { display: none }
</style>
</head>
<body>
<canvas id="canvas" width="786" height="259"></canvas>
<!-- 引入插件(需要的话自行下载引入)-->
<script src="https://yaoyuan.vip/js/Plugins/gsap.min.js"></script>
<script>
"use strict";
function _toConsumableArray(e) {
return _arrayWithoutHoles(e) || _iterableToArray(e) || _unsupportedIterableToArray(e) || _nonIterableSpread()
}
function _nonIterableSpread() {
throw new TypeError(
"Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."
)
}
function _unsupportedIterableToArray(e, r) {
if (e) {
if ("string" == typeof e) return _arrayLikeToArray(e, r);
var t = Object.prototype.toString.call(e).slice(8, -1);
return "Object" === t && e.constructor && (t = e.constructor.name), "Map" === t || "Set" === t ? Array.from(e) :
"Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(e, r) : void 0
}
}
function _iterableToArray(e) {
if ("undefined" != typeof Symbol && null != e[Symbol.iterator] || null != e["@@iterator"]) return Array.from(e)
}
function _arrayWithoutHoles(e) {
if (Array.isArray(e)) return _arrayLikeToArray(e)
}
function _arrayLikeToArray(e, r) {
(null == r || r > e.length) && (r = e.length);
for (var t = 0, a = new Array(r); t < r; t++) a[t] = e[t];
return a
}
function _classCallCheck(e, r) {
if (!(e instanceof r)) throw new TypeError("Cannot call a class as a function")
}
function _defineProperties(e, r) {
for (var t = 0; t < r.length; t++) {
var a = r[t];
a.enumerable = a.enumerable || !1, a.configurable = !0, "value" in a && (a.writable = !0), Object
.defineProperty(e, a.key, a)
}
}
function _createClass(e, r, t) {
return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), e
}
var config = { src: "https://npm.elemecdn.com/guli-heo/others/open-peeps-sheet.png", rows: 15, cols: 7 },
randomRange = function(e, r) {
return e + Math.random() * (r - e)
},
randomIndex = function(e) {
return 0 | randomRange(0, e.length)
},
removeFromArray = function(e, r) {
return e.splice(r, 1)[0]
},
removeItemFromArray = function(e, r) {
return removeFromArray(e, e.indexOf(r))
},
removeRandomFromArray = function(e) {
return removeFromArray(e, randomIndex(e))
},
getRandomFromArray = function(e) {
return e[0 | randomIndex(e)]
},
resetPeep = function(e) {
var r, t, a = e.stage,
n = e.peep,
o = .5 < Math.random() ? 1 : -1,
i = 100 - 250 * gsap.parseEase("power2.in")(Math.random()),
s = a.height - n.height + i;
return 1 == o ? (r = -n.width, t = a.width, n.scaleX = 1) : (r = a.width + n.width, t = 0, n.scaleX = -1), n.x =
r, n.y = s, {
startX: r,
startY: n.anchorY = s,
endX: t
}
},
normalWalk = function(e) {
var r = e.peep,
t = e.props,
a = (t.startX, t.startY),
n = t.endX,
o = gsap.timeline();
return o.timeScale(randomRange(.5, 1.5)), o.to(r, {
duration: 10,
x: n,
ease: "none"
}, 0), o.to(r, {
duration: .25,
repeat: 40,
yoyo: !0,
y: a - 10
}, 0), o
},
walks = [normalWalk],
Peep = function() {
function e(r) {
var t = r.image,
a = r.rect;
_classCallCheck(this, e), this.image = t, this.setRect(a), this.x = 0, this.y = 0, this.anchorY = 0, this
.scaleX = 1, this.walk = null
}
return _createClass(e, [{
key: "setRect",
value: function(e) {
this.rect = e, this.width = e[2], this.height = e[3], this.drawArgs = [this.image]
.concat(_toConsumableArray(e), [0, 0, this.width, this.height])
}
}, {
key: "render",
value: function(e) {
e.save(), e.translate(this.x, this.y), e.scale(this.scaleX, 1), e.drawImage.apply(e,
_toConsumableArray(this.drawArgs)), e.restore()
}
}]), e
}(),
img = document.createElement("img");
img.onload = init, img.src = config.src;
var canvas = document.querySelector("#canvas"),
ctx = canvas.getContext("2d"),
stage = {
width: 0,
height: 0
},
allPeeps = [],
availablePeeps = [],
crowd = [];
function init() {
createPeeps(), resize(), gsap.ticker.add(render), window.addEventListener("resize", resize)
}
function createPeeps() {
for (var e = config.rows, r = config.cols, t = e * r, a = img.naturalWidth / e, n = img.naturalHeight / r, o =
0; o < t; o++) allPeeps.push(new Peep({
image: img,
rect: [o % e * a, (o / e | 0) * n, a, n]
}))
}
function resize() {
stage.width = canvas.clientWidth, stage.height = canvas.clientHeight, canvas.width = stage.width * devicePixelRatio,
canvas.height = stage.height * devicePixelRatio, crowd.forEach((function(e) {
e.walk.kill()
})), crowd.length = 0, availablePeeps.length = 0, availablePeeps.push.apply(availablePeeps, allPeeps),
initCrowd()
}
function initCrowd() {
for (; availablePeeps.length;) addPeepToCrowd().walk.progress(Math.random())
}
function addPeepToCrowd() {
var e = removeRandomFromArray(availablePeeps),
r = getRandomFromArray(walks)({
peep: e,
props: resetPeep({
peep: e,
stage: stage
})
}).eventCallback("onComplete", (function() {
removePeepFromCrowd(e), addPeepToCrowd()
}));
return e.walk = r, crowd.push(e), crowd.sort((function(e, r) {
return e.anchorY - r.anchorY
})), e
}
function removePeepFromCrowd(e) {
removeItemFromArray(crowd, e), availablePeeps.push(e)
}
function render() {
canvas.width = canvas.width, ctx.save(), ctx.scale(devicePixelRatio, devicePixelRatio), crowd.forEach((function(e) {
e.render(ctx)
})), ctx.restore()
}
</script>
</body>
</html>
相册(反向滚动)
自己对接下相册数据列表,点击后操作需要自己按照需求去添加。目前数据格式是这样的
[{
“link”: “图片链接”,
“desc”: “图片表述”,
“type”: “风景、西安”
},{
“link”: “xx”,
“desc”: “xxx😀”,
“type”: “xx”
}]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>people</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,minimal-ui">
<style>
html, body { height: 100%; }
body { overflow: hidden; }
.photoWall {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
}
.scrollsection {
padding: 10vh 10vh 10vh 10vmax;
min-width: 99999999999999vh;
pointer-events: none !important;
}
.scrollsection > .item {
display: inline-block;
position: relative;
margin: 0 -30vh 0 3vh;
}
.scrollsection > .item::before {
content: "";
display: inline-block;
vertical-align: middle;
height: 100%;
}
.scrollsection > .item.-big {
height: 80vh;
width: 60vh;
}
.scrollsection > .item.-big.-horizontal {
height: 60vh;
width: 80vh;
}
.scrollsection > .item.-normal {
height: 60vh;
width: 45vh;
z-index: 100;
}
.scrollsection > .item.-normal:nth-of-type(3n) {
bottom: 5vh;
}
.scrollsection > .item.-normal:nth-of-type(4n) {
bottom: -5vh;
}
.scrollsection > .item.-small {
height: 40vh;
width: 30vh;
z-index: 200;
}
.scrollsection > .item.-small.-horizontal {
height: 30vh;
width: 40vh;
}
.scrollsection > .item.-small:nth-of-type(3n) {
bottom: 13vh;
}
.scrollsection > .item.-small:nth-of-type(4n) {
bottom: -13vh;
}
.scrollsection > .item > .image {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
filter: grayscale(1);
opacity: 0;
pointer-events: none;
transform: translateX(0) translateY(0) scale(1);
transition: filter 0.3s ease, opacity 1s ease, transform 1s ease;
object-fit: contain;
}
.scrollsection > .item:nth-of-type(4n) > .image {
transform: translateX(-30vh) translateY(-30vh) scale(0.8);
transition-delay: 0;
}
.scrollsection > .item:nth-of-type(4n - 1) > .image {
transform: translateX(30vh) translateY(30vh) scale(0.7);
transition-delay: 0.05s;
}
.scrollsection > .item:nth-of-type(4n - 2) > .image {
transform: translateX(-30vh) translateY(30vh) scale(0.8);
transition-delay: 0.1s;
}
.scrollsection > .item.-normal.-horizontal {
height: 45vh;
width: 60vh;
}
.scrollsection > .item:nth-of-type(4n - 3) > .image {
transform: translateX(-30vh) translateY(-30vh) scale(0.8);
transition-delay: 0.15s;
}
.scrollsection > .item > .image.-active {
transform: translateX(0) translateY(0) scale(1);
opacity: 0.8;
pointer-events: auto;
transition: filter 0.3s ease, opacity 1s ease, transform 1s ease;
}
.scrollsection > .item > .image.-clicked {
transform: translateX(0) translateY(0) scale(5);
opacity: 0;
pointer-events: auto;
transition: filter 0.3s ease, opacity 1s ease, transform 1s ease;
}
.scrollsection > .item > .image.-active:hover {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
filter: grayscale(0);
opacity: 1;
cursor: pointer;
transform: translateX(0) translateY(0) scale(1.2);
z-index: 150;
}
.btn {
margin-top: 0;
margin-bottom: 10px;
display: inline-block;
position: relative;
background: linear-gradient(135deg, #6e87d6, #8b63e4);
color: #fff;
font-size: 16px;
font-weight: bold;
text-align: center;
text-decoration: none;
border-radius: 6px;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
overflow: hidden;
transition: all 0.3s ease;
border: none;
}
.btn a {
padding: 12px 15px !important;
line-height: 1.5rem !important;
color: #fff !important;
font-size: 16px !important;
font-weight: bold;
}
.btn:hover {
background: linear-gradient(135deg, #8b63e4, #6e87d6);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3);
transform: translateY(-2px);
}
.ripple {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
pointer-events: none;
overflow: hidden;
}
.ripple::before {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-image: radial-gradient(circle, #fff 10%, transparent 10.01%);
background-repeat: no-repeat;
background-position: 50%;
transform: scale(10, 10);
opacity: 0;
transition: transform 0.5s ease-out, opacity 1s ease-out;
}
.ripple-effect {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 0;
height: 0;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.7);
animation: ripple-animation 1s;
}
@keyframes ripple-animation {
from {
width: 0;
height: 0;
opacity: 0.7;
}
to {
width: 300px;
height: 300px;
opacity: 0;
}
}
</style>
</head>
<body>
<div class="photoWall">
<div class='scroll-animations-example' data-scroll-container>
<div class='scrollsection' data-scroll-section></div>
</div>
</div>
<!-- 引入插件(需要的话自行下载引入)-->
<script src="https://yaoyuan.vip/js/Plugins/locomotive-scroll.min.js"></script>
<script>
let scrollsection = document.querySelector('.scrollsection')
function getData() {
// 获取相册列表,这里数据以自己的方式对接,只做示例
return new Promise((resolve, reject) => {
fetch('https://yaoyuan.vip/photo/photoList.json')
.then(response => response.json())
.then(res => {
renderData(res)
const example = new Example({
root: document.querySelector('.scroll-animations-example')
});
});
})
}
function renderData(data) {
const fragment = document.createDocumentFragment();
for (let i = 0; i < data.length; i++) {
const div = document.createElement('div');
//const scroll = document.createElement('scroll-animations-example')
const img = document.createElement('img');
//scroll.setAttribute('data-scroll-section')
div.className = random('className')
div.setAttribute('data-scroll', true)
div.setAttribute('data-scroll-speed', i==0?'item -normal -horizontal': random('number', 1, 5))
img.className = 'image'
img.src = data[i].link;
img.style.maxWidth = 600;
div.appendChild(img);
//scroll.appendChild(div);
fragment.appendChild(div);
}
scrollsection.textContent = ""
scrollsection.appendChild(fragment);
}
function random(type, min, max) {
let r = Math.floor(Math.random() * (max - min + 1) + min)
let arr =['item -small -horizontal','item -big -horizontal','item -normal -horizontal', 'item -normal','item -small','item -big']
if(type == 'number'){
return r
}else{
return arr[Math.floor(Math.random() * (arr.length-1 - 0 + 1) + 0)]
}
}
getData()
class Example {
constructor(options) {
this.root = options.root;
this.init();
setTimeout(this.showImages.bind(this), 1000);
}
init() {
this.scroll = new LocomotiveScroll({
el: this.root,
direction: 'horizontal',
smooth: true,
lerp: 0.05,
tablet: { smooth: true },
smartphone: { smooth: true }
});
this.images = this.root.querySelectorAll('.image');
[].forEach.call(this.images, (image) => {
image.addEventListener('click', () => {
image.classList.add('-clicked');
// 点击后操作,自行修改
this.hideImages();
});
});
}
showImages() {
[].forEach.call(this.images, (image) => {
image.classList.remove('-clicked');
image.classList.add('-active');
});
}
hideImages() {
[].forEach.call(this.images, (image) => {
image.classList.remove('-active');
});
setTimeout(this.showImages.bind(this), 2000);
}
}
window.addEventListener('DOMContentLoaded', (event) => {
const example = new Example({
root: document.querySelector('.scroll-animations-example')
});
});
</script>
</body>
</html>
鼠标移动小挂件
这个跟屁虫和小狗的区别还是很大的,小狗有动画,可交互,这个单纯只是个跟屁虫。元素可随意更换,可以是文字,可以是表情,可以是Icon等等。这是我跟chatGPT软磨硬泡才搞出来的,他笨的我想揍人。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>people</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,minimal-ui">
<style>
html, body { height: 100%; }
body { overflow: hidden; }
.over-flash {
height: 100vh;
width: 100vw;
background-color: #f3d2d2;
position: fixed;
pointer-events: none;
top: 0;
left: 0;
}
#plane{
color: #fff;
font-size: 30px;
/* 绝对定位 */
position: absolute;
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<!-- 就是这里,这个樱桃可以改成任何元素 -->
<div class="over-flash"> <div id="plane">🍒</div> </div>
<script>
let plane = document.getElementById('plane');
let deg = 0, ex=0, ey = 0, vx = 0, vy = 0, count = 0;
window.addEventListener('mousemove',(e) => {
ex = e.x - plane.offsetLeft - plane.clientWidth / 2;
ey = e.y - plane.offsetTop - plane.clientHeight / 2;
deg = 360 * Math.atan(ey / ex) / (2*Math.PI) + 45;
if (ex < 0) {
deg += 180;
}
count = 0;
})
function draw(){
plane.style.transform = 'rotate(' + deg + 'deg)';
if ( count < 100 ) {
vx += ex / 100;
vy += ey / 100;
}
plane.style.left = vx + 'px';
plane.style.top = vy + 'px';
count++;
}
setInterval(draw, 1);
</script>
</body>
</html>