|
|

楼主 |
发表于 2025-5-5 09:58
|
显示全部楼层
帖子代码:
- <style>
- #tz { --state: running; margin: 30px 0; left: calc(50% - 81px); transform: translateX(-50%); width: clamp(600px, 90vw, 1400px); min-height: 80vh; aspect-ratio: 16/9; background: #99ddff; box-shadow: 2px 2px 10px rgba(0,0,0,.65); z-index: 1; position: relative; }
- #player { position: absolute; left: 20px; top: 20px; z-index: 9; clip-path: circle(45%); transition: filter .7s; cursor: pointer; transform-origin: 50% 100%; animation: swear 1s infinite alternate linear var(--state); }
- #player:hover { filter: hue-rotate(120deg); }
- #btnFs { right: 30px; top: 30px; color: #eee; }
- #btnFs:hover { color: red; }
- @keyframes swear {
- 30% { transform: rotate(-6deg); }
- 80% { transform: rotate(6deg); }
- }
- </style>
- <div id="tz">
- <audio id="aud" src="https://music.163.com/song/media/outer/url?id=534750252" autoplay loop></audio>
- <img id="player" src="https://638183.freep.cn/638183/small/260.webp" width="10%" title="播放/暂停" />
- </div>
- <script type="importmap">
- {
- "imports": {
- "three": "https://esm.sh/three@0.176.0?target=es2022",
- "three/addons/": "https://esm.sh/three@0.176.0/addons/"
- }
- }
- </script>
- <script type="module">
- import * as THREE from 'three';
- import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
- import { FS } from 'https://638183.freep.cn/638183/web/ku/fscreen.js';
- let camera, scene, renderer, mesh, mixer, dummy;
- const offset = 5000;
- const timeOffsets = new Float32Array( 1024 );
- for (let x = 0; x < 1024; x ++) {
- timeOffsets[x] = Math.random() * 3;
- }
- const clock = new THREE.Clock(true);
- init();
- function init() {
- camera = new THREE.PerspectiveCamera(60, tz.offsetWidth / tz.offsetHeight, 100, 10000);
- scene = new THREE.Scene();
- scene.background = new THREE.Color(0x99DDFF);
- scene.fog = new THREE.Fog(0x99DDFF, 5000, 10000);
- const light = new THREE.DirectionalLight(0xffffff, 1);
- light.position.set(200, 1000, 50);
- light.castShadow = true;
- light.shadow.camera.left = - 5000;
- light.shadow.camera.right = 5000;
- light.shadow.camera.top = 5000;
- light.shadow.camera.bottom = - 5000;
- light.shadow.camera.far = 2000;
- light.shadow.bias = - 0.01;
- light.shadow.camera.updateProjectionMatrix();
- scene.add(light);
- const hemi = new THREE.HemisphereLight(0x99DDFF, 0x669933, 1 / 3);
- scene.add(hemi);
- const ground = new THREE.Mesh(
- new THREE.PlaneGeometry(1000000, 1000000),
- new THREE.MeshStandardMaterial({color: 0x669933, depthWrite: true})
- );
- ground.rotation.x = - Math.PI / 2;
- ground.receiveShadow = true;
- scene.add(ground);
- const loader = new GLTFLoader();
- loader.load('https://638183.freep.cn/638183/web/3models/Horse.glb', function (glb) {
- dummy = glb.scene.children[0];
- mesh = new THREE.InstancedMesh(dummy.geometry, dummy.material, 1024);
- mesh.castShadow = true;
- for (let x = 0, i = 0; x < 32; x ++) {
- for (let y = 0; y < 32; y ++) {
- dummy.position.set(offset - 300 * x + 200 * Math.random(), 0, offset - 300 * y);
- dummy.updateMatrix();
- mesh.setMatrixAt(i, dummy.matrix);
- mesh.setColorAt(i, new THREE.Color( `hsl(${Math.random() * 360}, 50%, 66%)`));
- i ++;
- }
- }
- scene.add(mesh);
- mixer = new THREE.AnimationMixer(glb.scene);
- const action = mixer.clipAction(glb.animations[0]);
- action.play();
- tz.onclick = () => action.paused = aud.paused; //马停
- });
- renderer = new THREE.WebGLRenderer({antialias: true});
- renderer.setPixelRatio(window.devicePixelRatio);
- renderer.setSize(tz.offsetWidth, tz.offsetHeight);
- renderer.setAnimationLoop(animate);
- tz.appendChild(renderer.domElement);
- renderer.shadowMap.enabled = true;
- renderer.shadowMap.type = THREE.VSMShadowMap;
- window.addEventListener('resize', onWindowResize);
- }
- function onWindowResize() {
- camera.aspect = tz.offsetWidth / tz.offsetHeight;
- camera.updateProjectionMatrix();
- renderer.setSize(tz.offsetWidth, tz.offsetHeight);
- }
- function animate() {
- //if (aud.paused) return; //动画停
- render();
- }
- function render() {
- const time = clock.getElapsedTime();
- const r = 3000;
- camera.position.set(Math.sin(time / 10) * r, 1500 + 1000 * Math.cos(time / 5), Math.cos(time / 10) * r);
- camera.lookAt(0, 0, 0);
- if (mesh) {
- for (let x = 0; x < 1024; x ++) {
- mixer.setTime(time + timeOffsets[x]);
- mesh.setMorphAt(x, dummy);
- }
- mesh.morphTexture.needsUpdate = true;
- }
- renderer.render(scene, camera);
- }
- FS(tz, player);
- </script>
复制代码 动态效果源自 three.js 的一个示例,马匹建模是 three.js 官方开源的数据。JS资源借用 esm.sh 上的部署。
我在代码中加入马匹奔跑、暂停的机制。若需要整个动画都可以暂停、继续,可以解开下面代码的注释:
//if (aud.paused) return; //动画停
同时,将马匹暂停、继续的代码注释掉:
tz.onclick = () => action.paused = aud.paused; //马停
整个动画暂停/继续机制不是很理想,表现在继续的时候,由于动画渲染的速度太快,衔接有点突兀。
|
评分
-
| 参与人数 2 | 威望 +80 |
金钱 +160 |
经验 +80 |
收起
理由
|
花飞飞
| + 30 |
+ 60 |
+ 30 |
很给力! |
红影
| + 50 |
+ 100 |
+ 50 |
赞一个! |
查看全部评分
|