美文网首页页面特效
CSS+HTML<3D无人机>

CSS+HTML<3D无人机>

作者: 誰在花里胡哨 | 来源:发表于2020-08-22 11:48 被阅读0次
效果图:
20200822.gif

主要是因为前几天买了一架大疆无人机(😁这里没算打广告啊,大疆就是好~~~),然后公司这几天也不是很忙,所以就想着搞一个无人机模型。起初是想学下3D Max建一个无人机模型,后来不知道怎么。就想试试看看html能不能拼凑一个,虽然很费时间,但是我成功了!!


image.png

这里基本就是用的css+html的一些基础知识,我只不是通过vue框架来写的。主要用于组件化的封装,这样就会节省时间了。下面主要跟大家说下涉及到的知识点和实现思路~~~~

知识点:
1.css+html,涉及了一点js(主要是用来控制无人机的移动用的);
2.transform-style: preserve-3d; css的3d旋转属性;
3.perspective css的景深属性;
4.filter: drop-shadow ; css的滤镜阴影穿透;
5.svg的path路径填充和描边;
6.animation css动画;

实现思路:

image.png

主要讲一下怎么拼成的模型吧。其实并不难,主要是耗时间,慢慢的拼凑。分为3个部分:
机桨(yepian.vue),机身(shenti.vue),机架(jijia.vue),合成部份(index.vue)。

image.png image.png

机架和机桨可以合成一个组件,然后其它三个就是公用的了,只需要旋转下角度就可以了。

机桨(yepian.vue):
image.png
<template>
  <div id="yepian" :class="flying?'flying':''">
    <div class="yepian z">
      <div class="uav ye_t">
        <svg>
          <path
            id="svg_3"
            d="m0.27471,0.21492l49.39565,34.69736l0,164.41747l-49.45058,-43.14501l0.05493,-155.96983z"
          />
        </svg>
      </div>
      <div class="uav ye_b">
        <svg>
          <path
            id="svg_4"
            d="m-0.55553,-0.29852l49.8888,42.52069l0,108.44431l-18.66664,0l-31.22216,-150.965z"
          />
        </svg>
      </div>
    </div>
    <div class="yepian f">
      <div class="uav ye_t">
        <svg>
          <path
            id="svg_3"
            d="m0.27471,0.21492l49.39565,34.69736l0,164.41747l-49.45058,-43.14501l0.05493,-155.96983z"
          />
        </svg>
      </div>
      <div class="uav ye_b">
        <svg>
          <path
            id="svg_4"
            d="m-0.55553,-0.29852l49.8888,42.52069l0,108.44431l-18.66664,0l-31.22216,-150.965z"
          />
        </svg>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    flying: Boolean
  }
};
</script>

<style lang="scss" scoped>
.flying {
  animation: xuanzhuan .1s linear infinite;
  filter: blur(30px);
}
#yepian {
  transform: translateZ(45px);
  transform-style: preserve-3d;
  .uav {
    border: none !important;
    svg {
      width: 100%;
      height: 100%;
      path {
        opacity: 0.8;
        stroke-width: 1;
      }
    }
  }
}
@keyframes xuanzhuan {
  0% {
    transform: rotate(0deg) translateZ(45px);
  }
  100% {
    transform: rotate(360deg) translateZ(45px);
  }
}
.ye_t,
.ye_b {
  width: 50px;
  //   background: red;
  overflow: hidden;
}
.ye_t {
  height: 200px;
  path {
    fill: rgb(94, 94, 94);
  }
}
.ye_b {
  height: 151px;
  position: relative;
  top: -45px;
  transform-origin: 50% 0;
  transform: rotateX(-20deg) rotateY(-18deg) translateZ(8px) rotateZ(-3deg);
  path {
    fill: rgb(56, 56, 56);
  }
}
.yepian {
  transform-style: preserve-3d;
  position: relative;
  &.z {
    left: -5px;
    top: 50px;
    transform: rotateY(20deg);
  }
  &.f {
    left: 5px;
    top: -50px;
    transform: scaleY(-1) scaleX(-1) rotateY(20deg);
  }
}
</style>
机身(shenti.vue):
image.png
<template>
  <div id="shenti">
    <!-- 身体组件 -->
    <div class="uav shen_t">
      <svg>
        <path
          id="svg_1"
          d="m0.71429,138.29017l139.28571,-138.29017l227.14286,0l132.85714,138.57143l-122.85714,661.42857l-235.71429,0l-140.71429,-661.70983z"
        />
      </svg>
      <div class="uav shen_tr">
        <svg>
          <path
            id="svg_2"
            d="m0,-0.99554l347.14286,153.85268l0,518.57143l-347.14286,0l0,-672.42411z"
          />
        </svg>
      </div>
      <div class="uav shen_tl">
        <svg style="transform: scaleX(-1);">
          <path
            id="svg_2"
            d="m0,-0.99554l347.14286,153.85268l0,518.57143l-347.14286,0l0,-672.42411z"
          />
        </svg>
      </div>
      <div class="uav shen_b">
        <svg>
          <path
            id="svg_2"
            d="m0.71429,138.29017l139.28571,-138.29017l227.14286,0l132.85714,138.57143l-122.85714,661.42857l-235.71429,0l-140.71429,-661.70983z"
          />
        </svg>
        <div class="b_b" v-for="item in 3" :key="item" :style="`transform: scale(${.1*item + .4}) translateY(${-70*item}px);`">
          <div class="l uav-boder"></div>
          <div class="r uav-boder"></div>
        </div>
      </div>
      <div class="uav shen_ht">
        <svg>
          <path
            id="svg_4"
            d="m0.15986,-0.09215l235.45647,0.09215l-40.75344,150.68487l-162.0769,-0.45662l-32.62613,-150.32039z"
          />
        </svg>
      </div>
      <div class="uav shen_hb">
        <svg>
          <path
            id="svg_4"
            d="m0.15986,-0.09215l235.45647,0.09215l-40.75344,150.68487l-162.0769,-0.45662l-32.62613,-150.32039z"
          />
        </svg>
      </div>
      <div class="uav shen_hbr">
        <svg>
          <path id="svg_1" d="m-0.25,0.22656l349.75,-0.22656l-90,151l-259.75,-150.77344z" />
        </svg>
      </div>
      <div class="uav shen_hbl">
        <svg style="transform: scaleX(-1)">
          <path id="svg_1" d="m-0.25,0.22656l349.75,-0.22656l-90,151l-259.75,-150.77344z" />
        </svg>
      </div>
      <div class="uav logo">
        <svg>
          <path
            id="svg_4"
            d="m86.8605,43.58286l-21.74425,-21.7224l-42.79068,42.79068l40.93022,18.13953l-39.99998,47.44184l70.69765,-45.58138l-40.4651,-26.97673l33.37215,-14.09153z"
          />
          <path
            id="svg_5"
            d="m130.1163,43.11774l23.83713,65.2543l17.67441,-35.81394l21.39534,33.95347l22.32557,-63.7209l-26.0465,29.30231l-16.27906,-31.16278l-18.60464,30.69766l-24.30225,-28.51013z"
          />
          <path
            id="svg_6"
            d="m279.88368,64.51309l-31.04657,-33.35031l24.18603,50.69765l-21.86045,33.95348l29.76743,-22.79069l27.44185,22.32557l-18.60465,-37.20929l27.44185,-49.76742l-37.32549,36.14101z"
          />
        </svg>
      </div>
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style lang="scss" scoped>
#shenti {
  position: relative;
  transform-style: preserve-3d;
  .uav {
    border: none ;
    svg {
      width: 100%;
      height: 100%;
      path {
        stroke-width: 1;
        opacity: 0.9;
      }
    }
  }
}
.shen_t,
.shen_b {
  width: 501px;
  height: 801px;
  position: relative;
}
.shen_t {
  path {
    fill: #808080;
  }
}
.shen_b {
  transform: scale(0.76) translateZ(-173px) translateY(115px);
  position: absolute;
  bottom: 0;
  path {
    fill: #ccc;
  }
  .b_b {
    width: 300px;
    height: 30px;
    display: flex;
    justify-content: space-between;
    position: absolute;
    left: 110px;
    bottom: 250px;
    .l {
      width: 100px;
      height: 30px;
      background: #808080;
      transform: skewX(45deg);
    }
    .r {
      width: 100px;
      height: 30px;
      background: #808080;
      transform: skewX(-45deg);
    }
  }
}
.shen_tr,
.shen_tl {
  width: 350px;
  height: 672px;
  position: absolute;
  bottom: 0;
  path {
    fill: #a0a0a0;
  }
}
.shen_tr {
  right: -225px;
  transform-origin: 0 100%;
  transform: rotateY(100deg) rotateX(-10.5deg) rotateZ(-2deg) scaleX(0.5);
}
.shen_tl {
  left: -210px;
  transform-origin: 100% 100%;
  transform: rotateY(-100deg) rotateX(-11.5deg) rotateZ(2deg) scaleX(0.5);
}
.shen_ht,
.shen_hb {
  position: absolute;
  bottom: -150px;
  left: 141px;
  width: 240px;
  height: 151px;
}
.shen_ht {
  transform-origin: 50% 0;
  transform: rotateX(-60deg);
  path {
    fill: #a0a0a0;
  }
}
.shen_hb {
  transform-origin: 50% 0;
  transform: translateZ(-173px) translateY(-9px) scaleX(0.75) scaleY(0.58)
    rotateX(17deg);
  path {
    fill: #ccc;
  }
}
.shen_hbr,
.shen_hbl {
  width: 350px;
  height: 150px;
  position: absolute;
  bottom: -149px;
  path {
    fill: #a0a0a0;
  }
}
.shen_hbr {
  right: -225px;
  transform-origin: 0% 0%;
  transform: rotateY(100deg) rotateZ(-2deg) rotateX(-12deg) scaleY(0.53)
    scaleX(0.5);
}
.shen_hbl {
  left: -210px;
  transform-origin: 100% 0%;
  transform: rotateY(-100deg) rotateZ(2deg) rotateX(-8deg) scaleX(0.5)
    scaleY(0.53);
}
.logo {
  width: 350px;
  height: 150px;
  transform: scale(0.5) translateZ(1px);
  z-index: 6;
  position: absolute;
  left: 85px;
  bottom: 50px;
  path {
    fill: white;
  }
}
</style>
机架(jijia.vue):

这里我直接把机桨和机架合到了一起,方便后面的直接使用。


image.png
<template>
  <div id="jijia">
    <div :id="jiwei?'ji':''">
      <div class="uav ji-1 t"></div>
      <div class="uav ji-1 b"></div>
      <div class="uav ji-1 q"></div>
      <div class="uav ji-1 h"></div>
      <div class="uav ji-2 t"></div>
      <div class="uav ji-2 b"></div>
      <div class="uav ji-2 q"></div>
      <div class="uav ji-2 h"></div>
      <uav-yepian class="uav-yepian" :flying="flying"></uav-yepian>
    </div>
  </div>
</template>

<script>
import yepian from "./yepian";
export default {
  props: {
    flying: Boolean,
    jiwei: Boolean
  },
  components: {
    "uav-yepian": yepian
  }
};
</script>

<style lang="scss" scoped>
#jijia {
  position: relative;
  transform-style: preserve-3d;
  width: 3px;
  .uav{
      opacity: .7;
  }
}
#ji {
  transform-style: preserve-3d;
  transform-origin: 0 50%;
  transform: scaleX(0.7);
}
.uav-yepian {
  position: absolute;
  left: 400px;
  top: -430px;
}
.ji-1 {
  width: 600px;
  height: 50px;
  position: absolute;
  left: -300px;
  &.t {
    transform: skewX(-45deg);
    background: #a3a3a3;
  }
  &.b {
    transform: translateZ(-50px) skewX(-45deg);
    background: #ccc;
  }
  &.q {
    transform: rotateX(-90deg) translateY(25px) translateZ(-25px)
      translateX(25px);
    background: #b6b6b6;
  }
  &.h {
    width: 658px;
    transform: rotateX(-90deg) translateY(25px) translateZ(25px)
      translateX(-25px);
    background: #b6b6b6;
  }
}
.ji-2 {
  width: 60px;
  height: 150px;
  position: absolute;
  top: -100px;
  right: -408px;
  &.t {
    transform: skewX(-45deg);
    background: #a3a3a3;
  }
  &.b {
    transform: translateZ(-50px) skewX(-45deg);
    background: #ccc;
  }
  &.q {
    width: 50px;
    transform: rotateY(90deg) rotateX(-45deg) translateX(25px) translateZ(-25px)
      translateY(-10px) scaleY(0.95);
    background: #ccc;
  }
  &.h {
    width: 50px;
    height: 225px;
    transform: rotateY(90deg) rotateX(-45deg) translateX(25px) translateZ(-10px)
      translateY(-44px) scaleY(0.945);
    background: #ccc;
  }
}
</style>
合成部份(index.vue):
image.png
<template>
  <div class="background" :class="uav_line?'black':''">
    <div class="cover" v-if="!fly">
      <div class="center">
        <h1>start</h1>
      </div>
      <!-- <div class="btm-msg">
        <div>
          <p>W:垂直向上 上升</p>
          <p>S:垂直向下 下降</p>
          <p>A:水平向左 左转</p>
          <p>D:水平向右 右转</p>
        </div>
        <div>
          <p>I:向前偏移 前进</p>
          <p>K:向后偏移 后退</p>
          <p>J:向左偏移 左飞</p>
          <p>L:向右偏移 右飞</p>
        </div>
      </div>-->
    </div>
    <div
      class="body"
      :style="`filter: drop-shadow(0px ${uav_shadow}px ${uav_shadow/10}px ${fly&&uav_vertical>20?'rgba(0, 0, 0, 0.7)':'rgba(0, 0, 0, 0.0ƒ)'});`"
    >
      <!-- 无人机 -->
      <div
        @click="uavFly()"
        id="container"
        :style="`transform:translateY(${-uav_vertical}px) translateX(${uav_lr_to}px) translateZ(${-uav_qh_to}px) scale(.15) rotateX(${container_rotate}deg);`"
      >
        <div
          :id="uav_line?'uav':''"
          class="uav"
          :style="`transform: rotateZ(${uav_level}deg) rotateY(${uav_lr}deg) rotateX(${uav_qh}deg)`"
        >
          <uav-jijia :flying="fly" class="uav-jijia-qr"></uav-jijia>
          <uav-jijia :flying="fly" class="uav-jijia-ql"></uav-jijia>
          <uav-shenti></uav-shenti>
          <uav-jijia :flying="fly" :jiwei="true" class="uav-jijia-hr"></uav-jijia>
          <uav-jijia :flying="fly" :jiwei="true" class="uav-jijia-hl"></uav-jijia>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { shenti, jijia } from "./components/index";
export default {
  components: {
    "uav-shenti": shenti,
    "uav-jijia": jijia
  },
  data() {
    return {
      window_w: window.innerWidth,
      window_h: window.innerHeight,
      fly: false, //是否已经起飞
      uav_line: false, //是否转线条或者实物无人机
      container_rotate: 0,
      uav_shadow: 0, //默认无人机阴影位置
      uav_sensitivity: 2, //灵敏度 默认为1
      uav_level: 0, //无人机水平角度
      uav_vertical: 0, //无人机垂直高度
      uav_lr: 0, //无人机左右偏移
      uav_qh: 0, //无人机前后偏移
      uav_lr_to: 0, //无人机左右偏移飞行距离
      uav_qh_to: 0 //无人机前后偏移飞行距离
    };
  },
  mounted() {
    this.listenKeydown();
  },
  methods: {
    //监听键盘事件
    listenKeydown() {
      let that = this;
      let sen = that.uav_sensitivity;
      let level_num = 0; //初始水平偏移为 0
      let vertical_num = 0; //初始垂直偏移为 0
      let lr_num = 0; //初始左右偏移量为 0
      let qh_num = 0; //初始前后偏移量为 0
      let lr_ing = false; //左右偏移是否正在执行
      let qh_ing = false; //前后偏移是否正在执行
      document.addEventListener(
        "keydown",
        e => {
          var ev = e || window.event;
          console.log(ev.keyCode);
          switch (ev.keyCode) {
            //用户按 A 键,无人机水平往左旋转
            case 65:
              level_num = -1;
              break;
            //用户按 D 键,无人机水平往右旋转
            case 68:
              level_num = 1;
              break;
            //用户按 W 键,无人机垂直上升
            case 87:
              //达到最大高度 200 时不再上升
              if (that.uav_vertical > that.window_h / 2) {
                vertical_num = 0;
              } else {
                vertical_num = 1;
              }
              break;
            //用户按 S 键,无人机垂直下降
            case 83:
              //达到最低高度时不再下降
              if (that.uav_vertical < 100) {
                vertical_num = 0;
              } else {
                vertical_num = -1;
              }
              break;
            //用户按 J 键,无人机往左偏移
            case 74:
              if (that.uav_lr_to < -that.window_w / 3) {
                lr_num = 0;
              } else {
                lr_num = -0.5;
              }
              lr_ing = true;
              break;
            //用户按 L 键,无人机往右偏移
            case 76:
              if (that.uav_lr_to > that.window_w / 3) {
                lr_num = 0;
              } else {
                lr_num = 0.5;
              }
              lr_ing = true;
              break;
            //用户按 I 键,无人机往前偏移
            case 73:
              qh_num = 0.5;
              qh_ing = true;
              break;
            //用户按 K 键,无人机往后偏移
            case 75:
              qh_num = -0.5;
              qh_ing = true;
              break;
            //用户按 空格 键,转换为线条或者实体无人机
            case 32:
              that.uav_line = !that.uav_line;
              break;
            default:
              break;
          }
        },
        false
      );
      document.addEventListener(
        "keyup",
        e => {
          var ev = e || window.event;
          switch (ev.keyCode) {
            case 65:
              level_num = 0;
              break;
            case 68:
              level_num = 0;
              break;
            case 87:
              vertical_num = 0;
              break;
            case 83:
              vertical_num = 0;
              break;
            case 74:
              lr_num = -1;
              lr_ing = false;
              break;
            case 76:
              lr_num = 1;
              lr_ing = false;
              break;
            case 73:
              qh_num = 1;
              qh_ing = false;
              break;
            case 75:
              qh_num = -1;
              qh_ing = false;
              break;
            default:
              break;
          }
        },
        false
      );
      //为了实现无人机上升和旋转同时操作
      function init() {
        if (that.fly) {
          //水平垂直偏移处理
          that.uav_level += level_num * sen;
          that.uav_vertical += vertical_num * sen;
          //阴影偏移
          that.uav_shadow += vertical_num * sen;
          //如果正在执行,偏移时有最大值
          //左右偏移处理 达到临界值松开回正归0
          if (lr_ing) {
            that.uav_lr = Math.min(40, that.uav_lr + lr_num * sen);
            that.uav_lr = Math.max(-40, that.uav_lr + lr_num * sen);
            // that.uav_lr += lr_num * sen;
            //是否允许左右移动
            that.uav_lr_to += lr_num * sen * 2;
          } else {
            if (lr_num > 0) {
              if (0 <= that.uav_lr) {
                that.uav_lr -= lr_num * sen;
              }
            } else {
              if (that.uav_lr <= 0) {
                that.uav_lr += -lr_num * sen;
              }
            }
          }

          if (qh_ing) {
            that.uav_qh = Math.min(20, that.uav_qh + qh_num * sen);
            that.uav_qh = Math.max(-20, that.uav_qh + qh_num * sen);
            // that.uav_qh += qh_num * sen;
            //是否允许前后移动
            that.uav_qh_to += qh_num * sen * 5;
          } else {
            if (qh_num > 0) {
              if (0 <= that.uav_qh) {
                that.uav_qh -= qh_num * sen;
              }
            } else {
              if (that.uav_qh <= 0) {
                that.uav_qh += -qh_num * sen;
              }
            }
          }
        }

        window.requestAnimationFrame(init);
      }
      init();
    },
    //点击无人机起飞
    uavFly() {
      let container = document.getElementById("container");
      container.style.top = "20%";
      let timer = setInterval(() => {
        this.container_rotate += 1;
        if (80 <= this.container_rotate) {
          clearInterval(timer);
        }
      }, 10);
      setTimeout(() => {
        this.fly = true;
      }, 1000);
    }
  }
};
</script>
<style lang="scss" >
:root {
  --uavColor: #ccc;
  --uavBorder: white;
}
#uav {
  transform-origin: 50% 50%;
  .uav,
  .uav-boder {
    background: transparent !important;
    border: 5px solid var(--uavBorder);
    box-sizing: border-box;
    svg path {
      opacity: 1 !important;
      fill: transparent !important;
      stroke: var(--uavBorder);
      stroke-width: 5 !important;
    }
  }
}
</style>
<style lang="scss" scoped>
.cover {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  .center {
    position: relative;
    z-index: 99;
    width: 150px;
    height: 150px;
    border-radius: 50%;
    pointer-events: none;
    display: flex;
    justify-content: center;
    align-items: center;
    text-transform: uppercase;

    // animation: change 0.5s linear infinite alternate;
    &::before {
      content: "";
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      border-radius: 50%;
      background: white;
      animation: change 1s linear infinite alternate;
      opacity: 0.3;
    }
    &::after {
      opacity: 0.3;
      content: "";
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      border-radius: 50%;
      background: white;
      animation: change 1s linear infinite alternate-reverse;
    }
    h1 {
      position: relative;
      z-index: 5;
      font-size: 17px;
      letter-spacing: 0.2rem;
      font-weight: 100;
      color: white;
      text-shadow: 0 0 3px black;
    }
  }
  @keyframes change {
    0% {
      transform: scale(1);
    }
    100% {
      transform: scale(0.2);
    }
  }
  .btm-msg {
    width: 100%;
    padding: 50px 100px;
    box-sizing: border-box;
    position: absolute;
    bottom: 0;
    display: flex;
    justify-content: space-between;
    p {
      font-size: 12px;
      font-weight: bold;
      margin-bottom: 5px;
    }
  }
}
.background {
  // 整体背景颜色
  background: #f1f1f1;
  transition: .3s;
  &.black{
    background: rgb(34, 34, 34);
  }
  .body {
    width: 100%;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    overflow: hidden;
    perspective: 10000;
  }
}
//无人机容器
#container {
  cursor: pointer;
  transform-style: preserve-3d;
  position: relative;
  top: 0;
  transition: top 0.5s;
}
.uav {
  transform-style: preserve-3d;
}
.shenti {
  width: 100px;
  height: 200px;
  background: #ccc;
}
.dibu {
  width: 100px;
  height: 100px;
  background: #808080;
  transform-origin: 0 0;
  transform: rotateX(-50deg);
}

.uav-jijia-qr {
  left: 570px;
  transform: rotateZ(-33deg) translateZ(-50px) translateY(200px);
}
.uav-jijia-ql {
  left: -70px;
  transform: rotateZ(33deg) translateZ(-50px) translateY(200px) scaleX(-1);
}
.uav-jijia-hr {
  left: 585px;
  transform: rotateZ(47deg) translateZ(-80px) translateY(130px) scaleY(-1);
}
.uav-jijia-hl {
  left: -77px;
  transform: rotateZ(-47deg) translateZ(-80px) translateY(130px) scaleY(-1)
    scaleX(-1);
}
</style>

相关文章

网友评论

    本文标题:CSS+HTML<3D无人机>

    本文链接:https://www.haomeiwen.com/subject/gcfvjktx.html