Browse Source

lego

master
jiannibang 6 years ago
parent
commit
b17ed22924
  1. 2
      package.json
  2. 81
      src/App.js
  3. 7
      src/App.scss
  4. 145
      src/components/shop.js
  5. 73
      src/components/shop.scss
  6. 402
      src/data/Shops.js
  7. BIN
      src/images/lego/42078.jpg
  8. BIN
      src/images/lego/42083.jpg
  9. BIN
      src/images/lego/42093.jpg
  10. BIN
      src/images/lego/42096.jpg
  11. BIN
      src/images/lego/42100.jpg
  12. BIN
      src/images/lego/42107.jpg
  13. BIN
      src/images/lego/42110.jpg
  14. BIN
      src/images/lego/42111.jpg
  15. BIN
      src/images/lego/42115.jpg
  16. BIN
      src/images/lego/71043.jpg
  17. BIN
      src/images/lego/75181.jpg
  18. BIN
      src/images/lego/75192.jpg
  19. BIN
      src/images/lego/75218.jpg
  20. BIN
      src/images/lego/75234.jpg
  21. BIN
      src/images/lego/75237.jpg
  22. BIN
      src/images/lego/75252.jpg
  23. BIN
      src/images/lego/75254.jpg
  24. BIN
      src/images/lego/75274.jpg
  25. BIN
      src/images/lego/75276.jpg
  26. BIN
      src/images/lego/75283.jpg
  27. BIN
      src/images/lego/75286.jpg
  28. BIN
      src/images/lego/75288.jpg
  29. BIN
      src/images/lego/75291.jpg
  30. BIN
      src/images/lego/75293.jpg
  31. BIN
      src/images/lego/75945.jpg
  32. BIN
      src/images/lego/75946.jpg
  33. BIN
      src/images/lego/75947.jpg
  34. BIN
      src/images/lego/75950.jpg
  35. BIN
      src/images/lego/75953.jpg
  36. BIN
      src/images/lego/75954.jpg
  37. BIN
      src/images/lego/75955.jpg
  38. BIN
      src/images/lego/75956.jpg
  39. BIN
      src/images/lego/75957.jpg
  40. BIN
      src/images/lego/75958.jpg
  41. BIN
      src/images/lego/75966.jpg
  42. BIN
      src/images/lego/75968.jpg
  43. BIN
      src/images/lego/75969.jpg
  44. BIN
      src/images/lego/75979.jpg
  45. BIN
      src/images/lego/75980.jpg
  46. BIN
      src/images/lego/HarryPotter-logo.png
  47. BIN
      src/images/lego/Technic-Logo.png
  48. 11
      src/images/lego/image-helper.js
  49. BIN
      src/images/lego/star_wars_logo.png
  50. BIN
      src/images/swatch/1.jpg
  51. BIN
      src/images/swatch/10.jpg
  52. BIN
      src/images/swatch/2.jpg
  53. BIN
      src/images/swatch/3.jpg
  54. BIN
      src/images/swatch/4.jpg
  55. BIN
      src/images/swatch/5.jpg
  56. BIN
      src/images/swatch/6.jpg
  57. BIN
      src/images/swatch/7.jpg
  58. BIN
      src/images/swatch/8.jpg
  59. BIN
      src/images/swatch/9.jpg
  60. 4
      src/images/swatch/image-helper.js
  61. 0
      src/images/uniqlo/1.jpg
  62. 0
      src/images/uniqlo/10.jpg
  63. 0
      src/images/uniqlo/11.jpg
  64. 0
      src/images/uniqlo/12.jpg
  65. 0
      src/images/uniqlo/13.jpg
  66. 0
      src/images/uniqlo/14.jpg
  67. 0
      src/images/uniqlo/2.jpg
  68. 0
      src/images/uniqlo/3.jpg
  69. 0
      src/images/uniqlo/4.jpg
  70. 0
      src/images/uniqlo/5.jpg
  71. 0
      src/images/uniqlo/6.jpg
  72. 0
      src/images/uniqlo/7.jpg
  73. 0
      src/images/uniqlo/8.jpg
  74. 0
      src/images/uniqlo/9.jpg
  75. 11
      src/images/uniqlo/image-helper.js
  76. 0
      src/models/Sphere.js
  77. 76
      src/models/Wall.js
  78. 10
      yarn.lock

2
package.json

@ -9,12 +9,14 @@
"@testing-library/jest-dom": "^4.2.4", "@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2", "@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2", "@testing-library/user-event": "^7.1.2",
"bezier-easing": "^2.1.0",
"canvas-sketch-util": "^1.10.0", "canvas-sketch-util": "^1.10.0",
"matter-js": "^0.14.2", "matter-js": "^0.14.2",
"node-sass": "^4.13.1", "node-sass": "^4.13.1",
"react": "^16.13.1", "react": "^16.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-scripts": "3.4.1", "react-scripts": "3.4.1",
"react-swipeable": "^5.5.1",
"three": "^0.117.1", "three": "^0.117.1",
"touches": "^1.2.2" "touches": "^1.2.2"
}, },

81
src/App.js

@ -1,11 +1,11 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import * as posenet from "@tensorflow-models/posenet"; import * as posenet from "@tensorflow-models/posenet";
import Wall from "./wall/Wall";
import Sphere from "./wall/Sphere";
import Wall from "./models/Wall";
import Sphere from "./models/Sphere";
import createTouches from "touches"; import createTouches from "touches";
import "./App.scss"; import "./App.scss";
import images from "./images/image-helper";
import shops from "./data/Shops";
import Shop from "./components/shop";
const videoWidth = 600; const videoWidth = 600;
const videoHeight = 500; const videoHeight = 500;
const width = window.innerWidth; const width = window.innerWidth;
@ -15,23 +15,16 @@ const displacement = 600;
const POSENET_URL = const POSENET_URL =
"https://lg-cjdqwkbo-1256266248.cos.ap-shanghai.myqcloud.com/mobile-net/50/model-stride16.json"; "https://lg-cjdqwkbo-1256266248.cos.ap-shanghai.myqcloud.com/mobile-net/50/model-stride16.json";
const Mist = ({ style }) => (
<div className="mist" style={style}>
<div className="ball"></div>
<div className="text">
touch <br />
item
</div>
</div>
);
const App = () => { const App = () => {
const [posenetModel, setPosenetModel] = useState(undefined); const [posenetModel, setPosenetModel] = useState(undefined);
const [video, setVideo] = useState(undefined); const [video, setVideo] = useState(undefined);
const [shopIndex, setShopIndex] = useState(0);
const [wall, setWall] = useState(undefined); const [wall, setWall] = useState(undefined);
const [sphere, setSphere] = useState(undefined); const [sphere, setSphere] = useState(undefined);
const [list, setList] = useState([]); const [list, setList] = useState([]);
const [minutes, setMinutes] = useState(0);
const shop = shops[shopIndex];
const [itemData, setItem] = useState(null);
async function poseDetectionFrame() { async function poseDetectionFrame() {
if (posenetModel) { if (posenetModel) {
const poses = await posenetModel.estimatePoses(video, { const poses = await posenetModel.estimatePoses(video, {
@ -94,10 +87,11 @@ const App = () => {
}, [video]); }, [video]);
const initWall = () => { const initWall = () => {
const { items, imgWidth, imgHeight } = shop;
const wall = new Wall({ const wall = new Wall({
items: Object.values(images).map((url) => ({ url })),
imgWidth: 90,
imgHeight: 100,
items,
imgWidth,
imgHeight,
containerWidth: width, containerWidth: width,
containerHeight: height, containerHeight: height,
speed: 1, speed: 1,
@ -107,11 +101,13 @@ const App = () => {
setWall(wall); setWall(wall);
return wall; return wall;
}; };
useEffect(() => { useEffect(() => {
if (wall && sphere) { if (wall && sphere) {
wall.attachSphere(sphere); wall.attachSphere(sphere);
} }
}, [wall]); }, [wall]);
useEffect(() => { useEffect(() => {
if (!sphere) { if (!sphere) {
let sphere = new Sphere({ let sphere = new Sphere({
@ -126,6 +122,26 @@ const App = () => {
} }
}, [sphere]); }, [sphere]);
useEffect(() => {
const interval = setInterval(() => {
setMinutes((minutes) => minutes + 1);
}, 1000 * 60 * 60);
return () => clearInterval(interval);
}, []);
// useEffect(() => {
// if (minutes > 0) {
// wall.dispose().then(() => {
// setShopIndex((shopIndex + 1) % shops.length);
// setWall(null);
// });
// }
// }, [minutes]);
useEffect(() => {
initWall();
}, [shopIndex]);
// useEffect(() => { // useEffect(() => {
// if (sphere) { // if (sphere) {
// const container = document.querySelector(".App"); // const container = document.querySelector(".App");
@ -141,18 +157,29 @@ const App = () => {
// }, [sphere]); // }, [sphere]);
return ( return (
<div className="App">
{list.map(([key, el]) => (
<div <div
className="App"
onClick={async (e) => {
await wall.dispose();
setWall(null);
initWall();
className="block"
key={key}
style={el.style}
onClick={(e) => {
setItem({ dom: e.target, item: el.data, shop });
}} }}
>
{list.map(([key, el]) => (
<div className="block" key={key} style={el.style}></div>
></div>
))} ))}
{sphere && <Mist style={sphere.style}></Mist>}
{sphere && (
<div className="mist" style={sphere.style}>
<div className="ball"></div>
{itemData && <Shop data={itemData}></Shop>}
{!itemData && (
<div className="text">
touch <br />
item
</div>
)}
</div>
)}
</div> </div>
); );
}; };

7
src/App.scss

@ -1,9 +1,12 @@
* {
box-sizing: border-box;
}
.App { .App {
position: relative; position: relative;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
text-align: center; text-align: center;
background: rgb(218, 232, 255);
background: #f8f8f8;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
@ -20,6 +23,7 @@
background-position: center; background-position: center;
opacity: 1; opacity: 1;
position: absolute; position: absolute;
border: 1px solid rgb(224, 224, 224);
} }
.mist { .mist {
position: absolute; position: absolute;
@ -34,6 +38,7 @@
filter: blur(50px); filter: blur(50px);
border-radius: 50%; border-radius: 50%;
background: rgba(255, 255, 255, 0.8); background: rgba(255, 255, 255, 0.8);
pointer-events: none;
} }
.text { .text {
text-align: center; text-align: center;

145
src/components/shop.js

@ -0,0 +1,145 @@
import React, { useState, useEffect } from "react";
import { Swipeable } from "react-swipeable";
import "./shop.scss";
const itemVh = 380;
const Shop = ({ data }) => {
const { item, shop } = data;
const { name, items, imgWidth, imgHeight, categoryLogoMap } = shop;
const categoryUrl = categoryLogoMap[item.category];
const list = items.filter(({ category }) => category === item.category);
const [index, setIndex] = useState(null);
const itemVw = (itemVh / imgHeight) * imgWidth;
const [stage, setStage] = useState(null);
const [phStyle, setPhStyle] = useState(null);
const init = () => {
setPhStyle({
position: "absolute",
width: itemVw + "px",
height: itemVh + "px",
left: 0,
right: 0,
top: 0,
bottom: 0,
margin: "auto",
opacity: 1,
backgroundImage: `url(${item.url})`,
});
setTimeout(() => {
setStage(1);
}, 1000);
};
const entrance = () => {
setStage(2);
};
useEffect(() => {
if (stage !== null && stage !== 2) return;
const { dom, item } = data;
const { width, height, left, top } = dom.getBoundingClientRect();
setIndex(list.findIndex(({ id }) => item.id === id));
setStage(-1);
setTimeout(() => {
setPhStyle({
position: "fixed",
width: width + "px",
height: height + "px",
left: left + "px",
top: top + "px",
opacity: dom.style.opacity,
backgroundImage: `url(${item.url})`,
});
setStage(0);
});
}, [data]);
useEffect(() => {
switch (stage) {
case 0:
init();
break;
case 1:
entrance();
break;
default:
break;
}
}, [stage]);
const pre = () => setIndex((index - 1 + list.length) % list.length);
const nxt = () => setIndex((index + 1 + list.length) % list.length);
return (
<>
{(stage === 0 || stage === 1) && (
<div className="ph" style={phStyle}></div>
)}
{stage >= 1 && (
<div className="shop">
<div className="up">
<div className="title">{name}</div>
</div>
<div className="middle">
<div className="arrow left" onClick={pre}>
{"<"}
</div>
<Swipeable className="imgs" onSwipedLeft={nxt} onSwipedRight={pre}>
{list.map(({ url }, i) => {
let leftD = index - i;
let rightD = i - index;
if (leftD < 0) leftD += list.length;
if (rightD < 0) rightD += list.length;
const isCurrent = i === index;
const isLeft = leftD < rightD;
const dis = isCurrent ? 0 : isLeft ? leftD : rightD;
const scale = 1 - 0.25 * dis;
return (
<div
className={"img"}
style={{
zIndex: 2 - dis,
top: `${((1 - scale) / 2) * itemVh}px`,
width: `${scale * itemVw}px`,
height: `${scale * itemVh}px`,
backgroundImage: `url(${url})`,
opacity: dis <= 2 ? 1 : 0,
...(isCurrent
? {
left: `calc(50% - ${itemVw / 2}px)`,
}
: isLeft
? {
left:
dis === 2 ? 0 : `calc((100% - ${itemVw}px) / 4)`,
}
: {
left:
dis === 2
? `calc(100% - ${scale * itemVw}px)`
: `calc(100% - ((100% - ${itemVw}px) / 4) - ${
scale * itemVw
}px)`,
}),
}}
key={url}
></div>
);
})}
</Swipeable>
<div className="arrow right" onClick={nxt}>
{">"}
</div>
</div>
<div className="down">
<div className="texts">
<div className="name">
<img src={categoryUrl}></img> {list[index].name}
</div>
<div className="price">{list[index].price}</div>
<div>{list[index].desc}</div>
</div>
</div>
</div>
)}
</>
);
};
export default Shop;

73
src/components/shop.scss

@ -0,0 +1,73 @@
.shop {
z-index: 1;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
box-sizing: border-box;
padding: 100px;
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.up,
.down {
flex: 1;
display: flex;
flex-direction: column;
animation: fadeIn 0.5s ease-in-out;
.title {
font-size: 60px;
margin: auto;
}
.texts {
margin: auto;
}
}
.middle {
position: relative;
flex: 0 0 380px;
padding: 0 40px;
.arrow {
position: absolute;
top: 0;
bottom: 0;
margin: auto;
width: 40px;
line-height: 40px;
height: 40px;
font-size: 40px;
font-weight: bold;
&.left {
left: 0;
}
&.right {
right: 0;
}
}
.imgs {
position: relative;
width: 100%;
height: 100%;
.img {
transition: width 1s, height 1s, top 1s, left 1s;
position: absolute;
background-size: cover;
background-position: center;
margin: auto;
border: 1px solid rgb(224, 224, 224);
}
}
}
}
.ph {
position: fixed;
transition: all 1s ease-out;
background-position: center;
background-size: cover;
border: 1px solid rgb(224, 224, 224);
}

402
src/data/Shops.js

@ -0,0 +1,402 @@
import swatch from "../images/swatch/image-helper";
import uniqlo from "../images/uniqlo/image-helper";
import lego from "../images/lego/image-helper";
const shops = [
{
name: "LEGO",
categories: ["哈利·波特", "机械组", "星球大战"],
categoryLogoMap: {
"哈利·波特": lego["HarryPotter-logo"],
机械组: lego["Technic-Logo"],
星球大战: lego["star_wars_logo"],
},
items: [
{
id: 75968,
url: lego[75968],
name: "女贞路4号",
category: "哈利·波特",
desc: `乐高®哈利·波特女贞路 4 号 (75968) 将孩子们带入《哈利·波特》系列电影中最臭名昭著的房屋之一。
带有小人仔和魔法电影场景的玩具屋
哈利·波特粉丝们可以使用哈利罗恩·韦斯莱达力·德思礼弗农·德思礼佩妮·德思礼和多比小人仔体验无穷乐趣功能丰富的 2 层房屋能够为他们带来丰富多彩的创意乐趣其中包括找到哈利位于楼下的隐蔽橱柜通过信箱使霍格沃茨的来信涌入
可收藏的哈利·波特飞行汽车玩具
激动人心的魔法在屋外继续罗恩驾驶着会飞的福特安格里亚车出现在楼上窗户外孩子们可以打开行李箱将链子连接到钩子上窗户很快会被拉掉然后让哈利掏出来
选择最棒的哈利·波特礼物
如果你正在寻找最理想的儿童玩具那么乐高哈利·波特套装就是很好的选择其可以凭借可收藏的小人仔和神奇的功能带来丰富多彩的创意乐趣`,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75979,
url: lego[75979],
name: "海德薇",
category: "哈利·波特",
desc: `将海德薇的奇妙个性和优雅的动作带入现实生活,这只著名的白色猫头鹰源自《哈利·波特》系列电影。这款可收藏的乐高®哈利·波特海德薇 (75979) 展示玩具能够带来具有成就感的拼搭体验,并且展示起来非常引人注目。
可拼搭的猫头鹰海德薇 带有可飞翔的翅膀
这款积木拼搭版模型包含 630 块积木颗粒展示了飞翔中的海德薇它正在递送一封重要的信件海德薇的翼展为 34 厘米13 英寸拼搭完成后转动手柄会使铰接式翅膀优雅地上下扇动这款可收藏的模型站立在坚固的底板上配有可拆下的乐高®哈利·波特小人仔和另一只较小的展开翅膀的猫头鹰海德薇
可收藏的哈利·波特玩具可作为很棒的圣诞节或生日礼物
如果你正在为孩子或所有年龄段的哈利·波特粉丝们寻找炫酷的礼物或热门的可拼搭模型那么乐高哈利·波特系列玩具就是很好的选择一定能够带来丰富的魔法乐趣`,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75980,
url: lego[75980],
name: "陋居攻击",
category: "哈利·波特",
desc: `乐高®哈利·波特陋居攻击 (75980) 将富有想象力的玩具屋角色扮演与《哈利·波特》系列故事中最具戏剧性的场景之一结合起来。
食死徒会烧掉陋居吗
陋居韦斯莱一家的房屋包含如此多可供玩乐和探索的地方打开这款非常精致的玩具屋的一侧可为孩子提供更多玩乐空间包含 8 个小人仔玩乐可以立即开始孩子们可以重现这个著名的场景邪恶的食死徒贝拉特里克斯·莱斯特兰奇和芬里尔·格雷伯克进行攻击其中火元件可用来重现烧毁这座房屋的烈火
为孩子选择哈利·波特礼物
如果您正在寻找一套很酷的玩具那么乐高哈利·波特系列套装就很不错其基于源自深受人们喜爱的电影的精彩场景这些套装包含可收藏的小人仔熟悉的地点和令人惊叹的功能可将哈利·波特的魔法世界放到孩子手中`,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75969,
url: lego[75969],
name: "霍格沃茨天文塔",
category: "哈利·波特",
desc: `乐高®哈利·波特霍格沃茨天文塔 (75969) 包含许多源自《哈利·波特》系列电影的标志性地点、很酷角色和逼真细节。
充满哈利·波特电影动作的玩具积木城堡
玩乐这款乐高霍格沃茨玩具套装孩子们可以与哈利·波特赫敏·格兰杰霍拉斯·斯拉格霍恩卢娜·洛夫古德纳威·隆巴顿罗恩·韦斯莱拉文德·布朗 (Lavender Brown)德拉科·马尔福和海德薇一起自由探索魔法城堡中的有趣房间借助如此多熟悉的地点和受欢迎的角色孩子们可以重温如哈利·波特与混血王子等电影中的场景和故事编创属于自己的冒险
可收藏的玩具魔法小人仔及无止歇的行动
如果你正在为家庭成员或最要好的朋友寻找最理想的礼物那么乐高哈利·波特玩具就是很好的选择其能够为喜欢使用乐高积木进行拼搭和玩乐的孩子带来丰富多彩的创意乐趣`,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75966,
url: lego[75966],
name: "霍格沃茨有求必应屋",
category: "哈利·波特",
desc: `乐高®哈利·波特霍格沃茨有求必应屋 (75966) 可以将小男巫和女巫带入魔法世界,与一群朋友们一起进行秘密任务,对抗黑魔法。
哈利·波特与凤凰社角色扮演玩具
哈利·波特的粉丝们可以重现经典的电影时刻使用哈利赫敏·格兰杰和卢娜·洛夫古德创造属于他们自己的精彩冒险邓布利多的军队正在神秘的有求必应屋中练习咒语孩子们可以帮助哈利教授守护神咒本套装包含卢娜的兔子和赫敏的水獭能量发射器可放入小人仔的手中射击机械食死徒包含许多炫酷的功能和配件魔法乐趣永无止尽
神秘且丰富多彩的魔法冒险
乐高哈利·波特系列套装可以重现电影中动作精彩丰富的场景这些有趣的玩具套装包含受人喜爱的角色神话中的动物和熟悉的地点可以将魔法世界带入现实生活`,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 71043,
url: lego[71043],
name: "乐高®哈利·波特系列-霍格沃兹城堡(豪华收藏版)",
category: "哈利·波特",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75946,
url: lego[75946],
name: "三强争霸赛之匈牙利树蜂龙",
category: "哈利·波特",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75950,
url: lego[75950],
name: "乐高®哈利·波特系列-勇闯禁忌森林",
category: "哈利·波特",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75954,
url: lego[75954],
name: "乐高®哈利·波特系列-霍格沃茨城堡",
category: "哈利·波特",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75953,
url: lego[75953],
name: "乐高®哈利·波特系列-霍格沃茨城门与打人柳",
category: "哈利·波特",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75955,
url: lego[75955],
name: "乐高®哈利·波特系列-霍格沃茨特快列车",
category: "哈利·波特",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75956,
url: lego[75956],
name: "乐高®哈利·波特系列-魁地奇球赛",
category: "哈利·波特",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75958,
url: lego[75958],
name: "布斯巴顿魔法学校的飞行马车",
category: "哈利·波特",
desc: `玩乐这款乐高®哈利·波特 75958 布斯巴顿魔法学校的飞行马车拼搭套装,让你的想象力跟随马车一起飞翔。年轻的巫师们将会再现电影《哈利·波特与火焰杯》中惊心动魄的一幕:一辆华丽的飞行马车疾速赶往霍格沃茨,海格正在等候,准备引导马车安全着陆。一旦来到地面,可将这辆马车重新拼搭为 2 层的卧室和茶点区,这里是与马克西姆夫人、芙蓉·德拉库尔和加布丽·德拉库尔一起计划新冒险的理想地点。这款布斯巴顿魔法学校的马车套装包含 4 个小人仔和 2 匹飞马(拥有可以活动的翅膀、腿部和头部),可以把哈利·波特的粉丝们载往一个充满想象力的神奇世界。`,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75947,
url: lego[75947],
name: "海格小屋-营救巴克比克",
category: "哈利·波特",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75945,
url: lego[75945],
name: "疾疾护法现身",
category: "哈利·波特",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75957,
url: lego[75957],
name: "骑士巴士",
category: "哈利·波特",
desc: `让年轻的女巫或男巫们乘坐乐高®哈利·波特 75957 骑士巴士,体验旅行生活!当孩子们赶上紫色的三层巴士时,他们知道自己将会经历一次疯狂的旅行。这款神奇的玩具巴士游戏套装拥有无尽的玩乐可能,可通过它的铰接式侧部面板轻松进入内部,还可以拆下车顶,欣赏汽车顶层。一旦装好哈利的行李箱,就该紧紧抓住汽车了。当这辆汽车快速行驶、转弯躲闪和颠簸起伏时,孩子们会喜欢上床铺来回滑动和吊灯在顶篷上晃来晃去的样子。包含 3 个小人仔`,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 42111,
url: lego[42111],
name: "Dom's Dodge Charger 道奇Charger (速度与激情同款)",
category: "机械组",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 42115,
url: lego[42115],
name: "Lamborghini Sián FKP 37 跑车",
category: "机械组",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 42107,
url: lego[42107],
name: "杜卡迪 Ducati Panigale V4 R",
category: "机械组",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 42083,
url: lego[42083],
name: "BUGATTI Chiron®",
category: "机械组",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 42093,
url: lego[42093],
name: "Chevrolet Corvette ZR1 跑车",
category: "机械组",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 42110,
url: lego[42110],
name: "Land Rover Defender模型",
category: "机械组",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 42100,
url: lego[42100],
name: "Liebherr R 9800 挖掘机",
category: "机械组",
desc: `让这款由 4108 块积木组成的乐高机械组 Liebherr R 9800 挖掘机为你带来丰富多彩且激动人心的乐高®拼搭和玩乐体验。这款复刻版模型与 Liebherr 合作开发,其通过直观的乐高®机械组 CONTROL+ 应用程序进行控制,并由先进的智能集线器(配备 7 个电机)提供动力。先进的应用程序技术可实现超精确的运动和惊人的功能,同时通过 4 个带有炫酷图形的不同控制屏幕,还可提供逼真的数字游戏。这些多功能控制屏幕能够帮助用户控制机车向各个方向移动、转动上部结构、延伸并抬升吊杆、打开并倾斜铲斗、播放逼真的音效,以及获得实时反馈,如吊杆位置、功耗和驾驶距离。通过单触式屏幕,用户可以使用拖动模式来控制吊杆、挖掘臂和铲斗,而自定义拼搭屏幕能够使他们选择预设的指令或记录模型的动作,创建复杂的运动序列。并且,用户可以通过“挑战与成绩”界面完成挑战,解锁成就徽章。`,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 42096,
url: lego[42096],
name: "Porsche 911 RSR赛车",
category: "机械组",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 42078,
url: lego[42078],
name: "马克卡车 MACK Anthem",
category: "机械组",
desc: `通过这款乐高®机械组版马克卡车MACK Anthem仿真模型,探索世界顶尖的工程技术和设计。这款模型由乐高集团与马克卡车有限公司共同开发,拥有一系列仿真的技术细节和功能,包括前桥转向、后轮驱动和带有可移动活塞和可旋转散热器风扇的直列6缸发动机。这辆可拆离的拖车配有可伸缩的斜撑、支撑架、具有侧面装载能力的同步伸缩式起重臂,以及一个可装载的集中箱,其带有可锁闭的车门,而驾驶舱配有可打开的车门、可调座椅、精致的仪表板、方向盘、遮阳板和一张双层床。这款2合1高级拼搭套装还配有马克斗牛犬引擎盖标志,并可重拼为标志性的马克LR垃圾车。`,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75288,
url: lego[75288],
name: "AT-AT步行机",
category: "星球大战",
desc: `玩乐这款 AT-AT (75288) 乐高®儿童拼搭组合,重温霍斯战役及其它经典的《星球大战》三部曲场景。这款全地形装甲运输车辆的不同部分可以打开,以便于轻松玩乐,其还配有弹簧发射器和内部的飞行摩托 (speeder bike)。粉丝们还会喜欢上系列逼真的细节,如拉起卢克的绞车及他的热能手雷。
帝国对战义军联盟
这款动作丰富的套装包含 6 个乐高小人仔卢克·天行者维尔斯将军2 AT-AT 驾驶员和 2 名冲锋队员他们全配有武器其中包括卢克的光剑冲锋队员的三角架机枪能够启发有趣的星球大战角色扮演这款套装既适于单独玩乐也可以群体玩乐可作为乐高星球大战收藏者的很棒礼物
对乐高星球大战套装的喜爱之情
二十多年来乐高集团一直在重现星球大战世界中的标志性星际飞船交通工具地点和人物并且乐高星球大战已成为一个非常成功的主题精彩纷呈难以取舍`,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75274,
url: lego[75274],
name: "TIE 战斗机飞行员头盔",
category: "星球大战",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75276,
url: lego[75276],
name: "冲锋队员头盔",
category: "星球大战",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75286,
url: lego[75286],
name: "格里弗斯将军的星际战斗机",
category: "星球大战",
desc: `玩乐这款超棒的乐高®星球大战太空飞船儿童拼搭玩具:雷沃斯将军的星际战斗机 (75286),开启一个充满冒险的创意玩乐世界!粉丝们会喜欢上这些逼真且富有启发性的功能,如可打开的驾驶舱、弹簧发射器和可伸缩的起落架。
格雷沃斯将军对战欧比旺
这款拼搭套装包含 3 个配有武器的乐高®小人仔格雷沃斯将军欧比旺·克诺比和克隆人空降兵孩子们可以对星球大战西斯的复仇中的经典战斗进行角色扮演或者编创属于他们自己的故事这款拼搭组合非常适合单独或群体玩乐是乐高藏品的不错补充可作为年轻星球大战粉丝的超级礼物
乐高风格的星球大战行动
1999 年以来乐高集团一直致力于使用积木创造标志性的星球大战星际战机交通工具地点和人物它已成为一个非常成功的主题拥有一系列能够令所有年龄段粉丝心动不已的有趣拼装玩具`,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75291,
url: lego[75291],
name: "死星决战",
category: "星球大战",
desc: `玩乐这款乐高®星球大战:绝地归来拼搭套装,孩子们可以演绎卢克·天行者与达斯·维德之间的经典死星对决 (75291)。帕尔帕庭大帝的宫殿包含许多能够启发创意玩乐的很酷功能,其中包括乐高小人仔原力跳跃功能。楼梯和桥可以坍塌,达斯·维德还可以把大帝扔进反应堆坑井,就像经典情景一样。
行动角色扮演
这款死星拼搭组合能够提供测试性拼搭挑战还包含 5 个乐高星球大战小人仔配有光剑和长矛 (force pike)可供粉丝们进行动作丰富的战斗角色扮演本组合还附有密码可以解锁电子游戏乐高®星球大战天行者传奇中的角色
超棒的礼物
1999 年以来乐高集团一直致力于重现星球大战世界中标志性的星际飞船交通工具地点和人物乐高星球大战现在是其最成功的主题能够为不同年龄段的粉丝提供激动人心的礼物`,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75283,
url: lego[75283],
name: "装甲突击坦克 (AAT)",
category: "星球大战",
desc: `玩乐这款乐高®星球大战:克隆人战争装甲突击坦克 (AAT) 拼搭套装(75283),孩子们可以长时间体验经典战斗行动的乐趣。AAT 包含大量逼真的细节,其中包括可打开的舱门(内部可容纳战斗机器人)、乐高小人仔、2 个弹簧发射器及带有升降式大炮的可旋转炮塔,能够启发有趣且富有创意的玩乐。
501 军团大战战斗机器人
这款充满精彩行动的套装包含 2 个乐高星球大战小人仔阿索卡·塔诺和她的克隆人士兵AAT 驾驶员战斗机器人战斗机器人以及各式武器可供启发精彩的角色扮演这款玩具拼搭组合既适于单独玩乐也可以群体玩乐可作为一份超棒的礼物丰富粉丝们的乐高星球大战藏品
乐高风格的星球大战行动
1999 年以来乐高集团一直致力于使用积木创造标志性的星球大战星际战机交通工具地点和人物它已成为一个非常成功的主题拥有一系列能够令所有年龄段粉丝心动不已的套装`,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75252,
url: lego[75252],
name: "Imperial Star Destroyer™",
category: "星球大战",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75192,
url: lego[75192],
name: "豪华千年隼",
category: "星球大战",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75237,
url: lego[75237],
name: "帝国钛战机(4+版)",
category: "星球大战",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75234,
url: lego[75234],
name: "AT-AP 全地形攻击步行机",
category: "星球大战",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75254,
url: lego[75254],
name: "AT-ST步行机侵袭者",
category: "星球大战",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75218,
url: lego[75218],
name: "X-翼星际战机(经典战役版)",
category: "星球大战",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
{
id: 75181,
url: lego[75181],
name: "Y-翼星际战机",
category: "星球大战",
desc: ``,
price: Math.floor(Math.random() * 100) + 100,
},
],
imgWidth: 100,
imgHeight: 90,
},
{
name: "UNIQLO",
items: Object.values(uniqlo).map((url, i) => ({
id: i,
url,
name: "衣服" + i,
price: Math.floor(Math.random() * 100) + 100,
})),
imgWidth: 90,
imgHeight: 100,
},
{
name: "SWATCH",
items: Object.values(swatch).map((url, i) => ({
id: i,
url,
name: "手表" + i,
price: Math.floor(Math.random() * 500) + 200,
})),
imgWidth: 120,
imgHeight: 200,
},
];
export default shops;

BIN
src/images/lego/42078.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
src/images/lego/42083.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
src/images/lego/42093.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
src/images/lego/42096.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

BIN
src/images/lego/42100.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

BIN
src/images/lego/42107.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

BIN
src/images/lego/42110.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

BIN
src/images/lego/42111.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
src/images/lego/42115.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
src/images/lego/71043.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
src/images/lego/75181.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
src/images/lego/75192.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

BIN
src/images/lego/75218.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
src/images/lego/75234.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

BIN
src/images/lego/75237.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

BIN
src/images/lego/75252.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
src/images/lego/75254.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
src/images/lego/75274.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
src/images/lego/75276.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

BIN
src/images/lego/75283.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

BIN
src/images/lego/75286.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
src/images/lego/75288.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
src/images/lego/75291.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

BIN
src/images/lego/75293.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

BIN
src/images/lego/75945.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

BIN
src/images/lego/75946.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
src/images/lego/75947.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
src/images/lego/75950.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
src/images/lego/75953.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
src/images/lego/75954.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
src/images/lego/75955.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

BIN
src/images/lego/75956.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

BIN
src/images/lego/75957.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

BIN
src/images/lego/75958.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
src/images/lego/75966.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

BIN
src/images/lego/75968.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

BIN
src/images/lego/75969.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
src/images/lego/75979.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
src/images/lego/75980.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
src/images/lego/HarryPotter-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
src/images/lego/Technic-Logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

11
src/images/lego/image-helper.js

@ -0,0 +1,11 @@
function importAll(r) {
let obj = {};
r.keys().forEach((key) => {
obj[key.replace("./", "").replace(".jpg", "").replace(".png", "")] = r(key);
});
return obj;
}
const uniqlo = importAll(require.context("./", false, /\.(png|jpe?g|svg)$/));
export default uniqlo;

BIN
src/images/lego/star_wars_logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
src/images/swatch/1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

BIN
src/images/swatch/10.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
src/images/swatch/2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
src/images/swatch/3.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
src/images/swatch/4.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
src/images/swatch/5.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
src/images/swatch/6.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
src/images/swatch/7.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
src/images/swatch/8.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

BIN
src/images/swatch/9.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

4
src/images/image-helper.js → src/images/swatch/image-helper.js

@ -6,6 +6,6 @@ function importAll(r) {
return obj; return obj;
} }
const images = importAll(require.context("./", false, /\.(png|jpe?g|svg)$/));
const swatch = importAll(require.context("./", false, /\.(png|jpe?g|svg)$/));
export default images;
export default swatch;

0
src/images/1.jpg → src/images/uniqlo/1.jpg

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

0
src/images/10.jpg → src/images/uniqlo/10.jpg

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

0
src/images/11.jpg → src/images/uniqlo/11.jpg

Before

Width:  |  Height:  |  Size: 223 KiB

After

Width:  |  Height:  |  Size: 223 KiB

0
src/images/12.jpg → src/images/uniqlo/12.jpg

Before

Width:  |  Height:  |  Size: 277 KiB

After

Width:  |  Height:  |  Size: 277 KiB

0
src/images/13.jpg → src/images/uniqlo/13.jpg

Before

Width:  |  Height:  |  Size: 346 KiB

After

Width:  |  Height:  |  Size: 346 KiB

0
src/images/14.jpg → src/images/uniqlo/14.jpg

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

0
src/images/2.jpg → src/images/uniqlo/2.jpg

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

0
src/images/3.jpg → src/images/uniqlo/3.jpg

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

0
src/images/4.jpg → src/images/uniqlo/4.jpg

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

0
src/images/5.jpg → src/images/uniqlo/5.jpg

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

0
src/images/6.jpg → src/images/uniqlo/6.jpg

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 173 KiB

0
src/images/7.jpg → src/images/uniqlo/7.jpg

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

0
src/images/8.jpg → src/images/uniqlo/8.jpg

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

0
src/images/9.jpg → src/images/uniqlo/9.jpg

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 129 KiB

11
src/images/uniqlo/image-helper.js

@ -0,0 +1,11 @@
function importAll(r) {
let obj = {};
r.keys().forEach((key) => {
obj[key.replace("./", "").replace(".jpg", "")] = r(key);
});
return obj;
}
const uniqlo = importAll(require.context("./", false, /\.(png|jpe?g|svg)$/));
export default uniqlo;

0
src/wall/Sphere.js → src/models/Sphere.js

76
src/wall/Wall.js → src/models/Wall.js

@ -1,5 +1,9 @@
import { Vector3 } from "three"; import { Vector3 } from "three";
const g = 0.55 / 1000;
import BezierEasing from "bezier-easing";
const easeIn = BezierEasing(0.5, 0.16, 0.74, 0.38);
const easeOut = BezierEasing(0, 0, 0.58, 1);
const enterDuration = 3000;
const exitDuration = 5000;
class El { class El {
constructor(seed) { constructor(seed) {
const { data, row, wall, key, index } = seed; const { data, row, wall, key, index } = seed;
@ -16,10 +20,10 @@ class El {
scale = ((1 - displacementAmount / sphere.displacement) / 1) * 0.3 + 0.7; scale = ((1 - displacementAmount / sphere.displacement) / 1) * 0.3 + 0.7;
direction.setLength(displacementAmount); direction.setLength(displacementAmount);
direction.add(point); direction.add(point);
point.lerp(direction, 0.725); // ✨ magic number
point.lerp(direction, 0.725);
} }
if (point.distanceTo(targetPoint) > 0.01) { if (point.distanceTo(targetPoint) > 0.01) {
point.lerp(targetPoint, 0.27); // ✨ magic number
point.lerp(targetPoint, 0.27);
} }
const top = point.y - imgHeight / 2; const top = point.y - imgHeight / 2;
const left = point.x - imgWidth / 2; const left = point.x - imgWidth / 2;
@ -31,7 +35,8 @@ class El {
left: `${left}px`, left: `${left}px`,
backgroundImage: `url(${url})`, backgroundImage: `url(${url})`,
transform: `scale(${scale})`, transform: `scale(${scale})`,
zIndex: row.size - Math.floor(Math.abs(row.size / 2 - row.index)),
opcaity: scale,
zIndex: wall.rowNum - Math.floor(Math.abs(wall.rowNum / 2 - row.index)),
}; };
Object.assign(this, { seed, data, style, key, point }); Object.assign(this, { seed, data, style, key, point });
} }
@ -39,18 +44,22 @@ class El {
} }
class Row { class Row {
falled = false;
q = []; q = [];
est;
fst; fst;
t1; t1;
t2; t2;
constructor({ wall, index }) { constructor({ wall, index }) {
const initY = index * wall.blockHeight + wall.blockHeight / 2;
const offsetY = initY - wall.containerHeight - wall.blockHeight;
Object.assign(this, { Object.assign(this, {
wall, wall,
minSize: wall.colNum + 1,
minSize: wall.colNum + 2,
index, index,
ratio: Math.random(), ratio: Math.random(),
y: index * wall.blockHeight + wall.blockHeight / 2,
initY,
y: offsetY,
offsetY,
}); });
} }
get size() { get size() {
@ -85,14 +94,24 @@ class Row {
this.wall.elMap.set(el.key, new El(el.seed)); this.wall.elMap.set(el.key, new El(el.seed));
}); });
} }
fall(now) {
enter(now) {
if (!this.est) {
this.est = now;
} else {
this.y =
this.offsetY +
(this.wall.containerHeight + this.wall.blockHeight) *
easeOut((now - this.est) / enterDuration);
}
this.update();
}
exit(now) {
if (!this.fst) { if (!this.fst) {
this.fst = now; this.fst = now;
this.t1 = 0;
} else { } else {
this.t2 = now - this.fst;
this.y += (g * (this.t2 * this.t2 - this.t1 * this.t1)) / 2;
this.t1 = this.t2;
this.y =
this.initY +
this.wall.containerHeight * easeIn((now - this.fst) / exitDuration);
} }
this.update(); this.update();
} }
@ -113,7 +132,8 @@ class Row {
export default class Wall { export default class Wall {
#rafID = null; #rafID = null;
#lastTime; #lastTime;
falling = false;
entering = false;
exiting = false;
rows = []; rows = [];
key = 0; key = 0;
time = 0; time = 0;
@ -128,12 +148,12 @@ export default class Wall {
speed, speed,
onListChange, onListChange,
}) { }) {
const colNum = Math.floor(containerWidth / imgWidth);
const rowNum = Math.floor(containerHeight / imgHeight);
const colNum = Math.floor(containerWidth / imgWidth) - 1;
const rowNum = Math.floor(containerHeight / imgHeight) - 1;
const elNum = items.length; const elNum = items.length;
const ratioSpeed = speed / (containerWidth / colNum); const ratioSpeed = speed / (containerWidth / colNum);
const blockWidth = containerWidth / (colNum - 1);
const blockHeight = containerHeight / (rowNum - 1);
const blockWidth = containerWidth / colNum;
const blockHeight = containerHeight / rowNum;
const elMap = new Map(); const elMap = new Map();
Object.assign(this, { Object.assign(this, {
items, items,
@ -155,7 +175,7 @@ export default class Wall {
const { items } = this; const { items } = this;
return items[Math.floor(Math.random() * items.length)]; return items[Math.floor(Math.random() * items.length)];
} }
init() {
async init() {
const { rowNum } = this; const { rowNum } = this;
for (let i = 0; i < rowNum; i++) { for (let i = 0; i < rowNum; i++) {
const row = new Row({ wall: this, index: i }); const row = new Row({ wall: this, index: i });
@ -164,6 +184,13 @@ export default class Wall {
} }
this.#rafID = window.requestAnimationFrame(this.animate); this.#rafID = window.requestAnimationFrame(this.animate);
this.isRunning = true; this.isRunning = true;
this.entering = true;
await new Promise((resolve) => {
setTimeout(() => {
this.entering = false;
resolve();
}, enterDuration);
});
return this; return this;
} }
animate = () => { animate = () => {
@ -171,7 +198,11 @@ export default class Wall {
const now = performance.now(); const now = performance.now();
const dt = Math.min(this.maxDeltaTime, (now - this.#lastTime) / 1000); const dt = Math.min(this.maxDeltaTime, (now - this.#lastTime) / 1000);
this.rows.forEach((row) => this.rows.forEach((row) =>
this.falling ? row.fall(now) : row.nextFrame(this.ratioSpeed)
this.entering
? row.enter(now)
: this.exiting
? row.exit(now)
: row.nextFrame(this.ratioSpeed)
); );
this.getList(); this.getList();
this.time += dt; this.time += dt;
@ -180,17 +211,16 @@ export default class Wall {
}; };
async dispose() { async dispose() {
if (this.#rafID === null) return; if (this.#rafID === null) return;
this.falling = true;
this.exiting = true;
await new Promise((resolve) => { await new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
this.falling = false;
this.exiting = false;
window.cancelAnimationFrame(this.#rafID); window.cancelAnimationFrame(this.#rafID);
this.#rafID = null; this.#rafID = null;
this.isRunning = false; this.isRunning = false;
resolve(); resolve();
}, Math.sqrt((this.containerHeight * 2) / g) + 500);
}, exitDuration + 500);
}); });
return this; return this;
} }
getKey() { getKey() {

10
yarn.lock

@ -2146,6 +2146,10 @@ bcrypt-pbkdf@^1.0.0:
dependencies: dependencies:
tweetnacl "^0.14.3" tweetnacl "^0.14.3"
bezier-easing@^2.1.0:
version "2.1.0"
resolved "https://registry.npm.taobao.org/bezier-easing/download/bezier-easing-2.1.0.tgz#c04dfe8b926d6ecaca1813d69ff179b7c2025d86"
big.js@^5.2.2: big.js@^5.2.2:
version "5.2.2" version "5.2.2"
resolved "https://registry.npm.taobao.org/big.js/download/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" resolved "https://registry.npm.taobao.org/big.js/download/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
@ -7975,6 +7979,12 @@ react-scripts@3.4.1:
optionalDependencies: optionalDependencies:
fsevents "2.1.2" fsevents "2.1.2"
react-swipeable@^5.5.1:
version "5.5.1"
resolved "https://registry.npm.taobao.org/react-swipeable/download/react-swipeable-5.5.1.tgz#48ae6182deaf62f21d4b87469b60281dbd7c4a76"
dependencies:
prop-types "^15.6.2"
react@^16.13.1: react@^16.13.1:
version "16.13.1" version "16.13.1"
resolved "https://registry.npm.taobao.org/react/download/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" resolved "https://registry.npm.taobao.org/react/download/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"

Loading…
Cancel
Save