webGL(web图形库)是一个中JavaScript API,用于在web浏览器中呈现交互式2D与3D图形,而且无需使用插件。
ThreeJs 是一款WebGL框架(在其API接口基础上又进行了一层封装),
Three.js是基于原生WebGL封装运行的三维引擎
git地址
下载dev 压缩包
工程化开发,在vue里面npm下载three.js
import 引入
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../three.js-dev/build/three.js"></script>
<script src="../three.js-dev/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
<script>
//虚拟场景+相机+渲染器=效果
// //创建一个三维场景
const scene = new THREE.Scene()
// //创建一个几何体长宽高
const geometry = new THREE.BoxGeometry(100, 100, 100)
// //设置材质和颜色
const material = new THREE.MeshLambertMaterial({
transparent: true, //开启透明
opacity: 0.5, //设置透明度
color: 0xc708fd
}) //设置完会全黑,需要设置光源
// //创个一个网格模型对象
const mesh = new THREE.Mesh(geometry, material)
// //给网格模型添加到三维场景
scene.add(mesh);
// mesh.position.set(100, 100, 100) //设置在坐标轴的位置
// mesh.rotateX(Math.PI / 4) //旋转
//设置环境光源
const Ambient = new THREE.AmbientLight(0xffffff, 0.5); //环境光
scene.add(Ambient);
//设置点光源
const Point = new THREE.PointLight(0xffffff, 1);
Point.position.set(100, 100, 100); //200,200,200是在三个角的夹角
scene.add(Point);
//设置可视化点光源 一个白色三角 光照过来的位置
const pointLightHelper = new THREE.PointLightHelper(Point, 10)
scene.add(pointLightHelper)
//设置坐标轴
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
// //怎么呈现到web网页上:通过相机和渲染器把三维场景scene渲染出来
// //1.创建一个透视相机
const width = 800
const height = 500
//45:视场角度,width/height:canvas画布宽高比,1:近裁截面,3000,远裁截面
const camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000) //透视投影
camera.position.set(200, 200, 200) //相机所在位置
camera.lookAt(0, 0, 0) //相机所照方向 (0,0,0原点)位置+坐标会构成一条线决定你的观察方向
// //创建webGl渲染器
const renderer = new THREE.WebGLRenderer()
renderer.setSize(width, height) //设置渲染区域尺寸
renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
renderer.render(scene, camera) //渲染操作
document.body.appendChild(renderer.domElement) //body元素中插入canvas对象,相当于canvas的画布
//使用OrbitControls轨道控制器需要引入(可以360°转动)
const controls = new THREE.OrbitControls(camera, renderer.domElement)
// //使用OrbitControls轨道控制器需,重新进行更新
// controls.addEventListener('change', function () {
// renderer.render(scene, camera) //重新渲染
// })
//动画使用 requestAnimationFrame()实现动画效果,有动画渲染就不需要controls.addEventListener('change', function () {})
//动画的原理就是一张一张拼起来的照片,只要运动够快就是动画
//1.通过照相每次改变都需要从新呈现
const clock = new THREE.Clock() //获取上一次和这次渲染时间间隔
function render() {
const spt = clock.getDelta * 1000 //渲染时间间隔毫秒
const zl = 1000 / spt //渲染帧率 一秒渲染多少次 比如一秒渲染60次
renderer.render(scene, camera)
mesh.rotateY(0.01)
requestAnimationFrame(render)
}
render()
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../three.js-dev/build/three.js"></script>
<script src="../three.js-dev/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
<script>
//虚拟场景+相机+渲染器=效果
// //创建一个三维场景
const scene = new THREE.Scene()
// //创建一个几何体长宽高
const geometry = new THREE.BoxGeometry(100, 100, 100)
// //设置材质和颜色
const material = new THREE.MeshLambertMaterial({
transparent: true, //开启透明
opacity: 0.5, //设置透明度
color: 0xc708fd
}) //设置完会全黑,需要设置光源
// //创个一个网格模型对象
// const mesh = new THREE.Mesh(geometry, material)
// // //给网格模型添加到三维场景
// scene.add(mesh);
for (let i = 0; i < 10; i++) { //批量创建很多个立方体
for (let j = 0; j < 10; j++) {
const mesh = new THREE.Mesh(geometry, material)
mesh.position.set(i * 200, 0, j * 200, )
scene.add(mesh);
}
}
// mesh.position.set(100, 100, 100) //设置在坐标轴的位置
// mesh.rotateX(Math.PI / 4) //旋转
//设置环境光源
const Ambient = new THREE.AmbientLight(0xffffff, 0.5); //环境光
scene.add(Ambient);
//设置点光源
const Point = new THREE.PointLight(0xffffff, 1);
Point.position.set(100, 100, 100); //200,200,200是在三个角的夹角
scene.add(Point);
//设置可视化点光源 一个白色三角 光照过来的位置
// const pointLightHelper = new THREE.PointLightHelper(Point, 10)
// scene.add(pointLightHelper)
//设置坐标轴
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
// //怎么呈现到web网页上:通过相机和渲染器把三维场景scene渲染出来
// //1.创建一个透视相机
const width = 800
const height = 500
//45:视场角度,width/height:canvas画布宽高比,1:近裁截面,3000,远裁截面
const camera = new THREE.PerspectiveCamera(45, width / height, 1, 3000) //透视投影
camera.position.set(800, 800, 800) //相机所在位置
camera.lookAt(1000, 0, 1000) //相机所照方向 (0,0,0原点)位置+坐标会构成一条线决定你的观察方向
// lookAt会影响OrbitControls轨道控制器的区域
// //创建webGl渲染器
const renderer = new THREE.WebGLRenderer()
renderer.setSize(width, height) //设置渲染区域尺寸
renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
renderer.render(scene, camera) //渲染操作
document.body.appendChild(renderer.domElement) //body元素中插入canvas对象,相当于canvas的画布
//使用OrbitControls轨道控制器需要引入(可以360°转动)
const controls = new THREE.OrbitControls(camera, renderer.domElement)
controls.target.set(1000, 0, 1000)
controls.update()
// //使用OrbitControls轨道控制器需,重新进行更新
// controls.addEventListener('change', function () {
// renderer.render(scene, camera) //重新渲染
// })
//动画使用 requestAnimationFrame()实现动画效果,有动画渲染就不需要controls.addEventListener('change', function () {})
//动画的原理就是一张一张拼起来的照片,只要运动够快就是动画
//1.通过照相每次改变都需要从新呈现
const clock = new THREE.Clock() //获取上一次和这次渲染时间间隔
function render() {
const spt = clock.getDelta * 1000 //渲染时间间隔毫秒
const zl = 1000 / spt //渲染帧率 一秒渲染多少次 比如一秒渲染60次
renderer.render(scene, camera)
// mesh.rotateY(0.01)
requestAnimationFrame(render)
}
render()
</script>
</body>
</html>
//几大难点
1.太阳光源:方向光DirectionalLight
1.确认太阳位置:通过SunCalc.js js库 传入经纬度时间可以得出太阳的高度角,方位角,然后通过three的方法角度转弧度,转换成方向光的position
2.设置阴影的属性:密度,最远.最近距离,清晰度....
3.阴影水波纹的问题: this.directionalLight.shadow.bias = -0.01;(调整阴影偏差)
4.画模型加压缩:用SketchUp(草图大师),导出gltf模型,gltf-pipeline压缩,放到七牛云上
5.载入模型:gltf-pipeline压缩后使用DRACOLoader加载
6.网页端和移动端:mousedown,touchstart/touchmove/touchend
7.小程序镶嵌网页:日期格式,window获取不到问题
<template>
<div id="sunshine" style="width: 100vw; height: 100vh; overflow: hidden">
<div class="compass">
<img
src="../assets/images/compass.png"
class="icon"
style="transform: rotate(0deg)"
/>
</div>
<div class="page-wrappr" id="app2">
<div class="bottom-card">
<div class="top">
<h4 class="title">{{ newhouseName }}</h4>
<div class="date" style="float: right">
<span class="text"
><el-date-picker
v-model="date"
type="date"
:editable="false"
placeholder="选择日期"
format="yyyy/MM/dd"
value-format="yyyy/MM/dd"
@change="changDate"
@focus="focusDate"
>
</el-date-picker></span
><i class="van-icon van-icon-arrow"> </i>
</div>
</div>
<div class="time-wrapper">
<div class="play-btn play-btn-bf" @click="bfBtnFun()">
<svg
v-if="isPlay"
t="1689756307920"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="19031"
width="48"
height="48"
>
<path
d="M426.666667 138.666667v746.666666a53.393333 53.393333 0 0 1-53.333334 53.333334H266.666667a53.393333 53.393333 0 0 1-53.333334-53.333334V138.666667a53.393333 53.393333 0 0 1 53.333334-53.333334h106.666666a53.393333 53.393333 0 0 1 53.333334 53.333334z m330.666666-53.333334H650.666667a53.393333 53.393333 0 0 0-53.333334 53.333334v746.666666a53.393333 53.393333 0 0 0 53.333334 53.333334h106.666666a53.393333 53.393333 0 0 0 53.333334-53.333334V138.666667a53.393333 53.393333 0 0 0-53.333334-53.333334z"
fill="#f01e03"
p-id="19032"
></path>
</svg>
<svg
v-else
t="1689756265979"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="17742"
width="48"
height="48"
>
<path
d="M870.2 466.333333l-618.666667-373.28a53.333333 53.333333 0 0 0-80.866666 45.666667v746.56a53.206667 53.206667 0 0 0 80.886666 45.666667l618.666667-373.28a53.333333 53.333333 0 0 0 0-91.333334z"
fill="#f01e03"
p-id="17743"
></path>
</svg>
</div>
<div class="time-slider">
<span class="time">04:49</span>
<div class="van-slider van-slider" style="height: 4px">
<div
class="van-slider__bar"
style="width: 0; height: 4px; background: rgb(255, 204, 204)"
>
<div
role="slider"
tabindex="0"
class="van-slider__button-wrapper"
@mousedown.prevent="SunIconMousestartPC"
style="position: absolute; top: -15px; left: 0%"
v-if="isPc"
>
<div class="custom-button">
<img
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAAqCAYAAAAu9HJYAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAKaADAAQAAAABAAAAKgAAAACHgP6PAAAEK0lEQVRYCe1YSU8UQRSuYhpZ4gJoNDHxYMAFuWhiNCMHNQwICIgHjHr06kFvbhcuarh54De4kbgOLoBGDkgkGr2YqNF400hUQAzrMOX3qnuk6KnuqZ7p8WQlL/36vXrf+7q6a3nNWYhNNDVVskTikYS0rEb+8OGnMOALwgD5i7GwcIQJUSWF9JBauCQZK1J4qbpiDq6GTTI4A4OIsEnOKjlVXTEHV8MlGYncZJx/lEJ6SI3rcERHx3I2NnYNyVbAf5r397/R9QvDJurrtwPnCibbJCsvP8Z7en67cfUjOTHRiI4tCNyL6xMsLTvcgWHcO7hPnDwtzM6bBq0nWVT0DKP4U/YWogJr34Bobt6cFp2DQeIBFwQrJAzlo7yaZmlsjN+//x1PGSNyEoSA5udPou8pd3/R2WmxoSEa8XZIFLIeshryA/IFMgy5w2prB3lnZwK63QhPJWhZMcqbcqtX7TeZ6iBfRyJxF/drMbJHeV/fnZSPrvieOnC5hGRVql2r04Ri7Dy+7x7yi4aGdsRdhzrKLOsQdqfXZNc1X5IUgEkUYVNTK3lv71gKQLS1rYDtKu5bUrYA1zgrLT3O792bFAcPlkP/hcmy4BefkaQ7GATXgeBT2Le5fcb3nL9lJSV1IPrNJEY/cTwiMarL2PT0LbizJ0jYQtQQjsTzyKWaA5Fk4+OXkWCPCpC1TjiEZ9CMXzc+9I0g+A6yzADXrAvnc5iQWzEhP/sFmI9kMnk2VILEih5YiDN+BMlnRBJrYQGemNbBfLTDEt8H2YgkGx7eiSde64OTvYtwCd+nWViwK5l9ik4dUmcZTjBLjv7JZKUPRu4uG3/Ei4slaxL3jiHECWTepGSnrS6fzcan+kjDxex155OeAbaFfbNR97pdsXRQyGez8T24GK2T4sCBXXiQF3ljGYns5o8fj3jhm73uaPQllqBRL5Cc7IRL+D7NiCTOgUlg3PbBycV128H3xDAiKaM578JoznkiZeOwt8WuTKHGJJ39tTsTYEB/d6Z9m/CMScrkZWXnMJrPAxLRdyccwjNoRrNbxZGH3ulpqvBqVHsgPexDL5UP8pjvsJCn6ZISKrjigYgtdo7jVB4lHMKV5cmiT6v5jmTGQiwWO4LXfzFtK9OlokJMiAt8YOAmuZ1C7AbUb5kKMc9v0iFIJe0GSBFkvzu3TFhbWw2iMUg35BXkK4QOs3Sle7LHUNJWpwhKHMKzz5MbZF3v8wNCO5KitXUNm5l5D5DFwr2wMMofPPjgJprtvfw5MD8/vCRHcfEWXe2tH8nZ2X1LgqlwD5EgPZjEAy5GefFPCeXVND3JVavol3IcAIO41vkV7hpMY5ODW+fkiTM7b1q89nWn9TI0yEPr/3/mhqOVoVuqBKFuqp4hzN+t/yb9Y/65N2yS6n9yVc/pwcIlmad/5n8AC/ylWqliwkYAAAAASUVORK5CYII="
alt=""
class="sun-icon"
/>
</div>
</div>
<div
role="slider"
tabindex="0"
class="van-slider__button-wrapper"
@touchstart.prevent="SunIconMousestart"
@touchmove.prevent="SunIconMousemove"
@touchend.prevent="SunIconMouseend"
style="position: absolute; top: -15px; left: 0%"
v-else
>
<div class="custom-button">
<img
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAAqCAYAAAAu9HJYAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAKaADAAQAAAABAAAAKgAAAACHgP6PAAAEK0lEQVRYCe1YSU8UQRSuYhpZ4gJoNDHxYMAFuWhiNCMHNQwICIgHjHr06kFvbhcuarh54De4kbgOLoBGDkgkGr2YqNF400hUQAzrMOX3qnuk6KnuqZ7p8WQlL/36vXrf+7q6a3nNWYhNNDVVskTikYS0rEb+8OGnMOALwgD5i7GwcIQJUSWF9JBauCQZK1J4qbpiDq6GTTI4A4OIsEnOKjlVXTEHV8MlGYncZJx/lEJ6SI3rcERHx3I2NnYNyVbAf5r397/R9QvDJurrtwPnCibbJCsvP8Z7en67cfUjOTHRiI4tCNyL6xMsLTvcgWHcO7hPnDwtzM6bBq0nWVT0DKP4U/YWogJr34Bobt6cFp2DQeIBFwQrJAzlo7yaZmlsjN+//x1PGSNyEoSA5udPou8pd3/R2WmxoSEa8XZIFLIeshryA/IFMgy5w2prB3lnZwK63QhPJWhZMcqbcqtX7TeZ6iBfRyJxF/drMbJHeV/fnZSPrvieOnC5hGRVql2r04Ri7Dy+7x7yi4aGdsRdhzrKLOsQdqfXZNc1X5IUgEkUYVNTK3lv71gKQLS1rYDtKu5bUrYA1zgrLT3O792bFAcPlkP/hcmy4BefkaQ7GATXgeBT2Le5fcb3nL9lJSV1IPrNJEY/cTwiMarL2PT0LbizJ0jYQtQQjsTzyKWaA5Fk4+OXkWCPCpC1TjiEZ9CMXzc+9I0g+A6yzADXrAvnc5iQWzEhP/sFmI9kMnk2VILEih5YiDN+BMlnRBJrYQGemNbBfLTDEt8H2YgkGx7eiSde64OTvYtwCd+nWViwK5l9ik4dUmcZTjBLjv7JZKUPRu4uG3/Ei4slaxL3jiHECWTepGSnrS6fzcan+kjDxex155OeAbaFfbNR97pdsXRQyGez8T24GK2T4sCBXXiQF3ljGYns5o8fj3jhm73uaPQllqBRL5Cc7IRL+D7NiCTOgUlg3PbBycV128H3xDAiKaM578JoznkiZeOwt8WuTKHGJJ39tTsTYEB/d6Z9m/CMScrkZWXnMJrPAxLRdyccwjNoRrNbxZGH3ulpqvBqVHsgPexDL5UP8pjvsJCn6ZISKrjigYgtdo7jVB4lHMKV5cmiT6v5jmTGQiwWO4LXfzFtK9OlokJMiAt8YOAmuZ1C7AbUb5kKMc9v0iFIJe0GSBFkvzu3TFhbWw2iMUg35BXkK4QOs3Sle7LHUNJWpwhKHMKzz5MbZF3v8wNCO5KitXUNm5l5D5DFwr2wMMofPPjgJprtvfw5MD8/vCRHcfEWXe2tH8nZ2X1LgqlwD5EgPZjEAy5GefFPCeXVND3JVavol3IcAIO41vkV7hpMY5ODW+fkiTM7b1q89nWn9TI0yEPr/3/mhqOVoVuqBKFuqp4hzN+t/yb9Y/65N2yS6n9yVc/pwcIlmad/5n8AC/ylWqliwkYAAAAASUVORK5CYII="
alt=""
class="sun-icon"
/>
</div>
</div>
</div>
</div>
<span class="time">19:40</span>
</div>
<span class="current-time">10:43</span>
</div>
<div class="time-labels">
<span class="rise-time">日出时间</span
><span class="set-time">日落时间</span><span class="value"> </span>
</div>
<div class="dates">
<button class="btn" @click="jq(0)">大寒</button
><button class="btn" @click="jq(1)">春分</button
><button class="btn" @click="jq(2)">夏至</button
><button class="btn" @click="jq(3)">秋分</button
><button class="btn" @click="jq(4)">立冬</button
><button class="btn" @click="jq(5)">冬至</button>
</div>
</div>
</div>
</div>
</template>
<script>
import dayjs from "dayjs";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";//控制器
import * as SunCalc from "../assets/js/suncalc.js";//JS库用来获取太阳位置,日出日落时间
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";//glft模型
import { getSunshineNewhouse } from "@/api/house/newhouse";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";//gltf-pipeline压缩后使用DRACOLoader加载
export default {
name: "sunshine",
metaInfo() {
return {
title: "房大地-日照分析",
};
},
data() {
return {
imageUrl: "https://****.com/",
renderer: null,
latitude: 39.899746, //经纬度
longitude: 116.409337,
date: null,
camera: null, //相机
scene: null, //屏幕
ambientLight: null, //光照
directionalLight: null, //光照
SunIcon: null, //滑动块
vanSliderBar: null, //阴影进度条
vanSlider: null, //最长进度条
bfBtn: null, //播放按钮
ztBtn: null, //暂停按钮
HS: 0, //开始小时
MS: 0, //开始分钟
HL: 0, //结束小时
ML: 0, //结束分钟
timer: null, //计时器
sunrise: null, // 日出时间
sunset: null, // 日落时间
timeDifference: null, //时间差日落-日出
cube: null,
plane: null,
cube2: null,
controls: null, //性能插件
isPlay: true, //播放按钮
DTURL: "", //底图url
HouseURL: "", //3d模型url
building: null,
newhouseName: "",
offsetDegree: 0,
modelX: 0, //模型位置x
modelY: 0, //模型位置y
modelZ: 0, //模型位置z
modelScale: "", //模型比例
planeScale: "", //底图比例
x1: 0,
x2: 0, //滑块距离左边的位置
distance: 0,
isPc: true,
timerNum: 100, //
intervalNum: 10,
isDivMove: false, //小太阳是否拖拽
intersects: [], //几何体合计-点击事件
};
},
watch: {},
created() {},
mounted() {
//判断是什么环境,手机还是电脑
if (document.documentElement.clientWidth < 720) {
this.isPc = false;
this.timerNum = 1;
this.intervalNum = 100;
} else {
this.isPc = true;
this.timerNum = 1;
this.intervalNum = 10;
}
this.getSunshineFun();
},
methods: {
//获取3d模型和底图数据
getSunshineFun() {
getSunshineNewhouse(this.$route.params.id).then((res) => {
if (res.data) {
this.DTURL = this.imageUrl + res.data.backUrl;
this.HouseURL = this.imageUrl + res.data.modelUrl;
this.offsetDegree = res.data.offsetDegree;
this.latitude = res.data.newhouseLat;
this.longitude = res.data.newhouseLng;
this.newhouseName = res.data.newhouseName;
this.modelX = res.data.modelX;
this.modelY = res.data.modelY;
this.modelZ = res.data.modelZ;
this.modelScale = res.data.modelScale;
this.planeScale = res.data.planeScale;
//获取今天日期
var D = new Date();
this.date = new Date(D.getFullYear(), D.getMonth(), D.getDate());
this.SunIcon = document.querySelector(".van-slider__button-wrapper");
this.vanSliderBar = document.querySelector(".van-slider__bar");
this.vanSlider = document.querySelector(".van-slider");
this.bfBtn = document.querySelector(".play-btn-bf"); //播放按钮
this.ztBtn = document.querySelector(".play-btn-zt"); //暂停按钮
this.initObj();
this.initRender();
this.initScene();
this.initCamera();
this.initLight();
this.initModel();
this.initControls();
this.animate();
this.SunCalcGetTimes();
window.addEventListener("resize", this.onWindowResize);
}
});
},
//初始相机
initCamera() {
this.camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
1,
3000
);
this.camera.position.set(0, 300, 250); //相机所在的位置,默认为(0,0,0)个单位;
this.camera.lookAt(new THREE.Vector3(0, 0, 0));
//点击事件
},
//初始虚拟场景
initScene() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color("#b2e0ff");
this.scene.add(new THREE.AmbientLight(0xffffff, 1)); // 环境光
this.scene.receiveShadow = true;
},
//初始three 虚拟场景+相机+渲染器=场景
initRender() {
this.renderer = new THREE.WebGLRenderer({
antialias: true,
logarithmicDepthBuffer: true,
alpha: true,
});
this.renderer.setSize(window.innerWidth, window.innerHeight);
//告诉渲染器需要阴影效果
this.renderer.shadowMap.enabled = true;
this.renderer.outputEncoding = THREE.SRGBColorSpace; //RGB模式编码(sRGBEncoding)进行对材质进行渲染,SRGBColorSpace
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 默认的是,没有设置的这个清晰 THREE.PCFShadowMap
this.renderer.toneMapping = THREE.ReinhardToneMapping;
this.renderer.toneMappingExposure = 1.1; // 默认1为了让场景更明亮
document.querySelector("#sunshine").appendChild(this.renderer.domElement);
},
// 加载楼盘模型
initObj() {
let _this = this;
var loader = new GLTFLoader();
//gltf-pipeline压缩后使用DRACOLoader加载, gltf-pipeline -i 111.gltf -o modelDraco.gltf -d
const dracoLoader = new DRACOLoader();
// 设置Draco解码库 该路径就是解压文件所在的位置,https://www.gstatic.com/draco/versioned/decoders/1.5.6/或者把node_modules/three/examples/jsm/libs/draco文件复制到public文件下
dracoLoader.setDecoderPath("/draco/");
// 使用js方式解压
dracoLoader.setDecoderConfig({ type: "js" });
// 初始化_initDecoder 解码器
dracoLoader.preload();
// 设置gltf加载器dracoLoader解码器
loader.setDRACOLoader(dracoLoader);
loader.load(
this.HouseURL,
(gltf) => {
const model = gltf.scene;
gltf.scene.name = "模型1";
// 遍历模型中的所有子对象,设置阴影接收和投射属性
model.traverse(function (child) {
if (child.isMesh) {
// 重新设置材质
let scaleNum = _this.modelScale ? _this.modelScale : 2;
child.scale.set(scaleNum, scaleNum, scaleNum); // 设置模型大小缩放
model.position.x = _this.modelX != 0 ? _this.modelX : 300;
model.position.y = _this.modelY != 0 ? _this.modelY : 0;
model.position.z = _this.modelZ != 0 ? _this.modelZ : -300;
child.material.side = THREE.DoubleSide;
//物体遮挡阴影
child.castShadow = true;
child.receiveShadow = true;
}
});
_this.building = gltf.scene;
_this.scene.add(gltf.scene);
},
undefined,
function (error) {
console.error(error);
}
);
},
//底部草地+底图
initModel() {
//底图
var planeGeometry = new THREE.CircleGeometry(110, 64);
var texture = new THREE.TextureLoader().load(this.DTURL);
texture.encoding = THREE.SRGBColorSpace;
var planeMaterial = new THREE.MeshStandardMaterial({
map: texture,
});
this.plane = new THREE.Mesh(planeGeometry, planeMaterial);
this.plane.rotation.x = -0.5 * Math.PI;
this.plane.position.y = 0;
//物体遮挡阴影
this.plane.castShadow = true;
//告诉底部平面需要接收阴影
this.plane.receiveShadow = true;
this.scene.add(this.plane);
//草地
var planeGeometry1 = new THREE.CircleGeometry(150, 64);
var texture1 = new THREE.TextureLoader().load("/bj.png");
var planeMaterial1 = new THREE.MeshStandardMaterial({
map: texture1,
});
this.plane1 = new THREE.Mesh(planeGeometry1, planeMaterial1);
this.plane1.rotation.x = -0.5 * Math.PI;
this.plane1.position.y = -1;
//物体遮挡阴影
this.plane1.castShadow = true;
//告诉底部平面需要接收阴影
this.plane1.receiveShadow = true;
this.scene.add(this.plane1);
},
//日出日落时间
SunCalcGetTimes() {
if (typeof this.date == "string") {
this.date = new Date(this.date);
} else {
this.date = new Date(
this.date.getFullYear(),
this.date.getMonth(),
this.date.getDate()
); // 使用当前日期和时间
}
var times = SunCalc.getTimes(this.date, this.latitude, this.longitude);
let sunriseD = dayjs(times.sunrise);
let sunriseF = sunriseD.format("HH:mm");
// 日出
this.sunrise = sunriseF;
// 日落
this.sunset = dayjs(times.sunset).format("HH:mm");
document.querySelectorAll(".time")[0].innerHTML = this.sunrise;
document.querySelectorAll(".time")[1].innerHTML = this.sunset;
this.timeDifference = this.calculateTimeDifference(
this.sunrise,
this.sunset
);
document.querySelector(".time-labels .value").innerHTML =
"昼长:" +
this.timeDifference.hours +
"小时" +
this.timeDifference.minutes +
"分钟";
this.HS = this.sunrise.slice(0, 2) * 1;
this.MS = this.sunrise.slice(3, 5) * 1;
this.HL = this.sunset.slice(0, 2) * 1;
this.ML = this.sunset.slice(3, 5) * 1;
this.interval();
},
//计算太阳位置
SunCalcGetPosition(date) {
var sunPosition = SunCalc.getPosition(
date,
this.latitude,
this.longitude
);
// 太阳在Three.js坐标系中的位置向量
const sunDirection = new THREE.Vector3();
if (this.offsetDegree > 0) {
//角度转弧度
var offSetRad = THREE.MathUtils.degToRad(offsetDegree);
sunDirection.setFromSphericalCoords(
1,
Math.PI / 2 - sunPosition.altitude,
-sunPosition.azimuth - offSetRad
);
} else {
sunDirection.setFromSphericalCoords(
1,
Math.PI / 2 - sunPosition.altitude,
-sunPosition.azimuth
);
}
sunDirection.normalize();
var sunlightPosition = sunDirection.clone().multiplyScalar(200); //光源到原点的距离
//设置太阳位置
this.directionalLight.position.copy(sunlightPosition);
this.directionalLight.target.position.set(0, 0, 0);
},
//太阳光阴影
initLight(a, b) {
this.directionalLight = new THREE.DirectionalLight(0xffffff);
this.directionalLight.visible = true;
this.directionalLight.intensity = 3; //光线的密度,默认为1。 光照越强,物体表面就更明亮
this.directionalLight.shadow.camera.near = -300; //产生阴影的最近距离
this.directionalLight.shadow.camera.far = 300; //产生阴影的最远距离
this.directionalLight.shadow.camera.left = -300; //产生阴影距离位置的最左边位置
this.directionalLight.shadow.camera.right = 300; //最右边
this.directionalLight.shadow.camera.top = 300; //最上边
this.directionalLight.shadow.camera.bottom = -300; //最下面
this.directionalLight.shadow.bias = -0.01; //用于解决阴影水波纹条纹阴影的问题
this.directionalLight.shadow.mapSize.set(2048, 2048); //阴影清晰度
//告诉平行光需要开启阴影投射,物体遮挡阴影
this.directionalLight.castShadow = true;
this.scene.add(this.directionalLight);
},
//按住滑块拖动PC端
SunIconMousestart(e) {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
// 获取鼠标在当前事件源的位置
this.x1 = e.touches[0].clientX;
this.x2 = this.SunIcon.style.left.split("px")[0] * 1;
},
// 小太阳滑块PC端
SunIconMousemove(e) {
this.distance = e.touches[0].clientX - this.x1;
if (this.SunIcon.style.left.split("px")[0] * 1 <= 0) {
this.SunIcon.style.left = 0;
this.vanSliderBar.style.width = 0 + "px";
this.HS = this.sunrise.slice(0, 2) * 1;
this.MS = this.sunrise.slice(3, 5) * 1;
} else if (
this.SunIcon.style.left.split("px")[0] * 1 >=
this.vanSlider.clientWidth - this.SunIcon.clientWidth
) {
this.SunIcon.style.left =
this.vanSlider.clientWidth - this.SunIcon.clientWidth + "px";
this.vanSliderBar.style.width =
this.vanSlider.clientWidth - this.SunIcon.clientWidth + "px";
this.HS = this.sunset.slice(0, 2) * 1;
this.MS = this.sunset.slice(3, 5) * 1;
} else {
this.SunIcon.style.left = this.x2 + this.distance + "px";
this.vanSliderBar.style.width =
this.x2 + this.distance + this.SunIcon.clientWidth + "px";
}
//百分比
var bfb = (
(this.SunIcon.style.left.split("px")[0] * 1) /
(this.vanSlider.clientWidth - this.SunIcon.clientWidth)
).toFixed(2);
//进度条右侧时间
//最后时间
var l = this.sunset.slice(0, 2) * 60 + this.sunset.slice(3, 5) * 1;
//开始时间
var s = this.sunrise.slice(0, 2) * 60 + this.sunrise.slice(3, 5) * 1;
var a = (l - s) * bfb;
var h = Math.floor((a + s) / 60);
var m = ((s + a) % 60).toFixed(0) * 1;
this.HS = h;
this.MS = m;
document.querySelector(".current-time").innerHTML =
(h < 10 ? "0" + h : h) + ":" + (m < 10 ? "0" + m : m);
},
// 松开小太阳移动端
SunIconMouseend(e) {
this.isPlay = true;
this.interval();
},
//按住太阳拖拽移动端
SunIconMousestartPC(e) {
this.isDivMove = true;
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
// 获取鼠标在当前事件源的位置
var x1 = e.clientX - this.vanSlider.offsetLeft - this.SunIcon.offsetLeft;
var _this = this;
document.onmousemove = function (e) {
var x2 = e.clientX;
let distance = x2 - _this.vanSlider.offsetLeft - x1;
console.log(distance);
if (distance <= 0) {
distance = 0;
_this.HS = _this.sunrise.slice(0, 2) * 1;
_this.MS = _this.sunrise.slice(3, 5) * 1;
} else if (
distance >=
_this.vanSlider.clientWidth - _this.SunIcon.clientWidth
) {
distance = _this.vanSlider.clientWidth - _this.SunIcon.clientWidth;
_this.HS = _this.sunset.slice(0, 2) * 1;
_this.MS = _this.sunset.slice(3, 5) * 1;
} else {
_this.SunIcon.style.left = distance + "px";
_this.vanSliderBar.style.width =
distance + _this.SunIcon.clientWidth + "px";
}
//百分比
var bfb = (
distance /
(_this.vanSlider.clientWidth - _this.SunIcon.clientWidth)
).toFixed(2);
//进度条右侧时间
//最后时间
var l = _this.sunset.slice(0, 2) * 60 + _this.sunset.slice(3, 5) * 1;
//开始时间
var s = _this.sunrise.slice(0, 2) * 60 + _this.sunrise.slice(3, 5) * 1;
var a = (l - s) * bfb;
var h = Math.floor((a + s) / 60);
var m = ((s + a) % 60).toFixed(0) * 1;
_this.HS = h;
_this.MS = m;
document.querySelector(".current-time").innerHTML =
(h < 10 ? "0" + h : h) + ":" + (m < 10 ? "0" + m : m);
};
document.onmouseup = function (e) {
_this.isDivMove = false;
_this.isPlay = true;
_this.interval();
document.onmousemove = null;
document.onmouseup = null;
};
},
//初始化性能插件
initControls() {
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
// 使动画循环使用时阻尼或自转 意思是否有惯性
this.controls.enableDamping = true;
//是否可以缩放
this.controls.enableZoom = true;
//是否自动旋转
this.controls.autoRotate = false;
//设置相机距离原点的最远距离
this.controls.minDistance = 200;
//设置相机距离原点的最远距离
this.controls.maxDistance = 600;
//设置相机上下旋转最大角度最大到平面
this.controls.minPolarAngle = 0;
this.controls.maxPolarAngle = Math.PI / 2 - 0.1;
this.controls.addEventListener("change", (e) => {
// 获取对象的旋转角度;
const rotation = this.camera.rotation;
// 判断中心轴旋转方向
// 定义变量来跟踪旋转角度
let rotationDegrees = {
x: THREE.MathUtils.radToDeg(rotation.x),
y: THREE.MathUtils.radToDeg(rotation.y),
z: THREE.MathUtils.radToDeg(rotation.z),
};
//左上角指南针转动
document.querySelector(".compass img").style.transform =
"rotate(" + rotationDegrees.z + "deg)";
});
},
render() {
this.renderer.render(this.scene, this.camera);
},
//窗口变动触发的函数
onWindowResize() {
this.$nextTick(() => {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.render();
this.renderer.setSize(window.innerWidth, window.innerHeight);
});
},
animate() {
//更新控制器
this.render();
//更新性能插件
requestAnimationFrame(this.animate);
},
//计时器
interval() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
//控制分钟增加
this.timer = setInterval(() => {
if (this.MS >= 60) {
this.MS = 1;
this.HS = this.HS + 1;
} else {
this.MS += this.timerNum;
}
if (this.HS >= this.HL && this.MS >= this.ML) {
this.HS = this.sunrise.slice(0, 2) * 1;
this.MS = this.sunrise.slice(3, 5) * 1;
}
// 更新时间
this.date = new Date(
this.date.getFullYear(),
this.date.getMonth(),
this.date.getDate(),
this.HS,
this.MS
);
// 更新光照的方向和颜色
this.SunCalcGetPosition(this.date);
//计算相差时间比
//进度条右侧时间
const timeDifference2 = this.calculateTimeDifference(
this.sunrise,
this.HS + ":" + this.MS
);
let baifenb =
(
(timeDifference2.hours * 60 + timeDifference2.minutes) /
(this.timeDifference.hours * 60 + this.timeDifference.minutes)
).toFixed(2) * 100;
let currentTimeEle = document.querySelector(".current-time");
currentTimeEle.innerHTML =
(this.HS < 10 ? "0" + this.HS : this.HS) +
":" +
(this.MS < 10 ? "0" + this.MS : this.MS);
//设置阴影滚动条长度
this.vanSliderBar.style.width =
((this.vanSlider.clientWidth - this.SunIcon.clientWidth) * baifenb) /
100 +
"px";
this.SunIcon.style.left =
((this.vanSlider.clientWidth - this.SunIcon.clientWidth) * baifenb) /
100 +
"px";
}, this.intervalNum);
},
//点击播放按钮
bfBtnFun() {
console.log(this.isPlay);
if (this.isPlay) {
clearInterval(this.timer);
this.timer = null;
this.isPlay = false;
} else {
this.interval();
this.isPlay = true;
}
},
//计算两时间差
calculateTimeDifference(startTime, endTime) {
const start = new Date();
const end = new Date();
// 将时间字符串转换为 Date 对象
const [startHour, startMinute] = startTime.split(":");
const [endHour, endMinute] = endTime.split(":");
start.setHours(startHour, startMinute, 0);
end.setHours(endHour, endMinute, 0);
// 计算时间间隔(毫秒为单位)
const timeDiff = end.getTime() - start.getTime();
// 将毫秒转换为小时和分钟
const hours = Math.floor(timeDiff / (1000 * 60 * 60));
const minutes = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60));
return {
hours,
minutes,
};
},
//计算节气日期
jq(num) {
var solarTerm = new Array("小寒", "春分", "夏至", "秋分", "立冬", "冬至");
var sTermInfo = new Array(0, 107014, 240693, 375494, 440795, 504758);
function sTermDate(y, n) {
return new Date(
31556925974.7 * (y - 1900) +
sTermInfo[n] * 60000 +
Date.UTC(1900, 0, 6, 2, 5)
);
}
//获取2013年第一个节气小寒的公历日期
var Y = new Date().getFullYear();
var sterm = sTermDate(Y, num);
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
this.isPlay = true;
this.date = sterm;
this.SunCalcGetTimes();
},
//改变日期
changDate(e) {
var that = this;
var date = new Date(e);
var y = date.getFullYear(); // 年
var m = date.getMonth() + 1; // 月
m = m < 10 ? "0" + m : m;
var d = date.getDate(); // 日
d = d < 10 ? "0" + d : d;
that.date = y + "/" + m + "/" + d; //拼在一起
this.SunCalcGetTimes();
this.isPlay = true;
},
//日期选择器获取焦点
focusDate() {
//禁止软键盘弹出
document.activeElement.blur();
clearInterval(this.timer);
this.timer = null;
},
},
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
::v-deep .el-date-editor.el-input,
.el-date-editor.el-input__inner {
width: 150px;
}
#sunshine {
position: relative;
}
@media only screen and (min-width: 500px) {
.bottom-card {
position: absolute;
top: auto;
bottom: 0;
left: 50%;
width: 100vw;
transform-origin: center bottom;
transform: translateX(-50%) scale(0.46);
}
}
.bottom-card {
position: relative;
top: -4.26667vw;
padding: 4.26667vw 4vw;
background: #fff;
border-radius: 4.26667vw 4.26667vw 0 0;
z-index: 999;
color: #333330;
}
#app2 {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #333;
overflow: hidden;
font-size: 3.73333vw;
}
.page-wrapper {
position: relative;
height: 100vh;
overflow: hidden;
display: flex;
flex-flow: column nowrap;
background: #fff;
}
.canvas-container {
width: 100vw !important;
flex: 1;
}
.bottom-card {
position: relative;
top: -4.26667vw;
padding: 4.26667vw 4vw;
background: #fff;
border-radius: 4.26667vw 4.26667vw 0 0;
z-index: 999;
color: #333330;
}
.bottom-card .top {
display: flex;
justify-content: space-between;
line-height: 4.26667vw;
}
.bottom-card .top .title {
flex: 1;
font-size: 4.8vw;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
color: #333330;
line-height: 6.66667vw;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bottom-card .top .date {
display: flex;
justify-content: flex-end;
align-items: center;
font-size: 3.73333vw;
}
.top .date .text {
margin-right: 0.8vw;
}
.bottom-card .time-wrapper {
display: flex;
flex-flow: row nowrap;
align-items: center;
justify-content: center;
line-height: 5.33333vw;
}
.bottom-card .time-wrapper .play-btn {
width: 9.86667vw;
height: 9.86667vw;
font-size: 0;
margin-left: -1.6vw;
border: 1px solid #ff5d5d;
border-radius: 50%;
box-shadow: 0px 0px 5px #ffa2a2;
text-align: center;
line-height: 9.86667vw;
}
.bottom-card .time-wrapper .play-btn svg {
vertical-align: middle;
}
.van-slider {
position: relative;
width: 100%;
height: 2px;
background-color: #ebedf0;
border-radius: 999px;
cursor: pointer;
}
.bottom-card .time-wrapper .time-slider {
flex: 1;
display: flex;
align-items: center;
padding: 0 1.06667vw;
font-size: 3.73333vw;
}
.bottom-card .time-wrapper .time-slider .van-slider {
margin: 0 2.66667vw;
}
.bottom-card .time-wrapper .time-slider .van-slider .custom-button {
display: flex;
align-items: center;
justify-content: center;
}
.bottom-card .time-wrapper .time-slider .van-slider .custom-button .sun-icon {
width: 4.26667vw;
height: 4.26667vw;
}
.bottom-card .time-wrapper .current-time {
width: 19.46667vw;
height: 9.86667vw;
line-height: 9.86667vw;
text-align: center;
font-size: 3.2vw;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
color: #f44;
margin-right: -1.6vw;
background: url(data:image/png;base64,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);
background-size: 100% 100%;
}
.bottom-card .time-labels {
position: relative;
width: 65.86667vw;
margin-left: 8.26667vw;
font-size: 3.2vw;
text-align: center;
}
.bottom-card .time-labels .rise-time {
position: absolute;
left: -0.8vw;
}
.bottom-card .time-labels .set-time {
position: absolute;
right: -0.8vw;
}
.bottom-card .times {
display: flex;
flex-flow: row nowrap;
justify-content: space-around;
}
.bottom-card .times .value {
margin-left: 1.06667vw;
color: #ff3a3a;
}
.bottom-card .dates {
display: flex;
justify-content: space-between;
margin-top: 4vw;
}
.bottom-card .dates .btn {
flex: 1;
line-height: 6.4vw;
font-size: 3.46667vw;
text-align: center;
background: #eee;
border-radius: 1.06667vw;
color: #333330;
border: none;
padding: 0;
margin: 0 1.6vw;
}
.bottom-card .dates .btn:first-child {
margin-left: 0;
}
.bottom-card .dates .btn:last-child {
margin-right: 0;
}
.bottom-card .dates .active {
color: #fff;
background: #f44;
}
.compass {
position: absolute;
top: 5.33333vw;
left: 5.33333vw;
font-size: 0;
border-radius: 50%;
overflow: hidden;
z-index: 999;
}
.compass .icon {
width: 18.13333vw;
height: 18.13333vw;
}
.van-calendar {
padding-bottom: 6.4vw;
}
.ipx .bottom-card {
padding-bottom: 8.53333vw;
}
.ipx .van-calendar {
padding-bottom: 13.33333vw;
}
.user-guide {
position: absolute;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
z-index: 1000;
}
.user-guide .touch-guide {
width: 44.26667vw;
height: 42.66667vw;
}
.user-guide .close-btn {
width: 43.2vw;
height: 15.33333vw;
margin-top: 5.33333vw;
}
.css2DLabel {
position: relative;
font-size: 2.4vw;
line-height: 4vw;
border-radius: 0.53333vw;
color: #fff;
padding: 0 1.06667vw;
background: rgba(0, 0, 0, 0.4);
}
.css2DLabel:after {
content: "";
position: absolute;
top: 100%;
left: 50%;
width: 1px;
height: 4.26667vw;
border: none;
background: linear-gradient(180deg, #fff, hsla(0, 0%, 100%, 0) 80%);
}
@media only screen and (min-width: 500px) {
#app {
font-size: 24px;
}
.page-wrapper {
display: block;
}
.canvas-container {
width: 100vw !important;
height: 100vh !important;
}
.bottom-card {
position: absolute;
top: auto;
bottom: 0;
left: 50%;
width: 100vw;
transform-origin: center bottom;
transform: translateX(-50%) scale(0.46);
}
.compass {
top: 20px;
left: 20px;
}
.compass .icon {
width: 80px;
height: 80px;
}
.css2DLabel {
font-size: 12px;
line-height: 1.5;
border-radius: 4px;
padding: 0 6px;
}
.css2DLabel:after {
width: 1px;
height: 16px;
}
}
@media only screen and (min-width: 1200px) {
.bottom-card {
transform: translateX(-50%) scale(0.33);
}
.bottom-card .time-wrapper .time-slider .van-slider .custom-button {
padding: 0;
}
}
.loading {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 9999;
background-color: #313538;
}
.loading:before {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 2.66667vw;
width: 2.13333vw;
height: 2.13333vw;
color: #fff;
border-radius: 50%;
text-indent: -9999em;
animation: load-effect 1s linear infinite;
}
@keyframes load-effect {
0% {
box-shadow: 0 -3em 0 0.2em #fff, 2em -2em 0 0 #fff, 3em 0 0 -0.5em #fff,
2em 2em 0 -0.5em #fff, 0 3em 0 -0.5em #fff, -2em 2em 0 -0.5em #fff,
-3em 0 0 -0.5em #fff, -2em -2em 0 0 #fff;
}
12.5% {
box-shadow: 0 -3em 0 0 #fff, 2em -2em 0 0.2em #fff, 3em 0 0 0 #fff,
2em 2em 0 -0.5em #fff, 0 3em 0 -0.5em #fff, -2em 2em 0 -0.5em #fff,
-3em 0 0 -0.5em #fff, -2em -2em 0 -0.5em #fff;
}
25% {
box-shadow: 0 -3em 0 -0.5em #fff, 2em -2em 0 0 #fff, 3em 0 0 0.2em #fff,
2em 2em 0 0 #fff, 0 3em 0 -0.5em #fff, -2em 2em 0 -0.5em #fff,
-3em 0 0 -0.5em #fff, -2em -2em 0 -0.5em #fff;
}
37.5% {
box-shadow: 0 -3em 0 -0.5em #fff, 2em -2em 0 -0.5em #fff, 3em 0 0 0 #fff,
2em 2em 0 0.2em #fff, 0 3em 0 0 #fff, -2em 2em 0 -0.5em #fff,
-3em 0 0 -0.5em #fff, -2em -2em 0 -0.5em #fff;
}
50% {
box-shadow: 0 -3em 0 -0.5em #fff, 2em -2em 0 -0.5em #fff,
3em 0 0 -0.5em #fff, 2em 2em 0 0 #fff, 0 3em 0 0.2em #fff,
-2em 2em 0 0 #fff, -3em 0 0 -0.5em #fff, -2em -2em 0 -0.5em #fff;
}
62.5% {
box-shadow: 0 -3em 0 -0.5em #fff, 2em -2em 0 -0.5em #fff,
3em 0 0 -0.5em #fff, 2em 2em 0 -0.5em #fff, 0 3em 0 0 #fff,
-2em 2em 0 0.2em #fff, -3em 0 0 0 #fff, -2em -2em 0 -0.5em #fff;
}
75% {
box-shadow: 0 -3em 0 -0.5em #fff, 2em -2em 0 -0.5em #fff,
3em 0 0 -0.5em #fff, 2em 2em 0 -0.5em #fff, 0 3em 0 -0.5em #fff,
-2em 2em 0 0 #fff, -3em 0 0 0.2em #fff, -2em -2em 0 0 #fff;
}
87.5% {
box-shadow: 0 -3em 0 0 #fff, 2em -2em 0 -0.5em #fff, 3em 0 0 -0.5em #fff,
2em 2em 0 -0.5em #fff, 0 3em 0 -0.5em #fff, -2em 2em 0 0 #fff,
-3em 0 0 0 #fff, -2em -2em 0 0.2em #fff;
}
to {
box-shadow: 0 -3em 0 0.2em #fff, 2em -2em 0 0 #fff, 3em 0 0 -0.5em #fff,
2em 2em 0 -0.5em #fff, 0 3em 0 -0.5em #fff, -2em 2em 0 -0.5em #fff,
-3em 0 0 -0.5em #fff, -2em -2em 0 0 #fff;
}
}
@media only screen and (min-width: 500px) {
.loading:before {
font-size: 12px;
width: 12px;
height: 12px;
}
.bottom-card .time-wrapper .time-slider .van-slider .custom-button {
padding: 0;
}
::v-deep .el-date-editor.el-input,
.el-date-editor.el-input__inner {
transform: scale(2.5);
}
.bottom-card .top .title {
flex: none;
}
.bottom-card .top .date {
margin-right: 100px;
}
}
@media only screen and (min-width: 300px) and (max-width: 800px) {
#sunshine .page-wrappr {
position: absolute;
bottom: -20px;
border-radius: 20px 20px 0 0;
width: 100%;
}
}
.bottom-card .time-wrapper .play-btn svg {
width: 4.8vw;
height: 4.8vw;
}
.bottom-card .time-wrapper .time-slider .van-slider .custom-button {
padding-left: 0;
padding-right: 0;
}
</style>