three.js学习ing+日照效果

Three.js是基于原生WebGL封装运行的三维引擎

  • webGL
  • three.js
  • 1.下载
    • npm
  • 1.可旋转立方体
  • 可旋转立方体群
  • 日照

webGL

webGL(web图形库)是一个中JavaScript API,用于在web浏览器中呈现交互式2D与3D图形,而且无需使用插件。

three.js

ThreeJs 是一款WebGL框架(在其API接口基础上又进行了一层封装),
Three.js是基于原生WebGL封装运行的三维引擎

1.下载

git地址
下载dev 压缩包

npm

工程化开发,在vue里面npm下载three.js
import 引入

1.可旋转立方体

three.js学习ing+日照效果_第1张图片

 <!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>

可旋转立方体群

three.js学习ing+日照效果_第2张图片

three.js学习ing+日照效果_第3张图片

<!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>

日照

three.js学习ing+日照效果_第4张图片

//几大难点
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>

你可能感兴趣的:(three)