No puedes simplemente pescar, usa css3 para decirte cómo criar peces

Estoy participando en la competencia individual del Concurso de creatividad de juegos de la comunidad de Nuggets. Para obtener más información, consulte: Concurso de creatividad de juegos

introducir

En este número, usaremos css3+vue para hacer un pequeño juego de piscicultura. Aquí no usamos ningún material. Todos están dibujados por CSS (tos, aunque es un pez de papel). Cuando hacemos clic en la pantalla , podemos poner comida. Cuando vean comida, lucharán por ella, y los peces se harán más grandes cuando coman la comida. Porque es un pequeño juego de ocio y desarrollo, no hay nada que no se pueda eliminar. Depende sobre quién tiene el pez más grande.

Sin más preámbulos, primero vayamos a Kangkang para mostrar el efecto:

VID_20220404_212850.gif

Dirección de demostración: codepen.io/jsmask/full…

comienzo

dibujar estanque de peces

<div class="main" ref ="pool"></div> 
复制代码
.main{
    width: 100%;
    height: 100vh;
    background: linear-gradient(180deg, #86defc 0%, #71cceb 20%, #73b2f1, #349ef8 83%, #cce293 93%, #e6cd6a 100%);
    overflow: hidden;
    position: relative;
}
复制代码

El fondo se dibuja con un degradado lineal para hacer un degradado, y se puede hacer una capa. Es relativamente claro al principio y luego continúa haciéndose más profundo. En la parte inferior, se agrega una parte de amarillo claro como arena de fondo.

WeChat captura de pantalla_20220404213300.png

dibujar peces pequeños

<div class="fish" ref="fish" v-for="(item,index) in fishList" :key="index" 
     :class="{'left':item.direction==-1,[item.type]:true}" 
     :style="{'transform':'translate('+item.x+'px,'+item.y+'px)'}">
    <div class="fish-main" :style="{'transform':'scale('+(1+item.level*0.025)+')'}">
        <div class="fish-body">
            <div class="fish-fins"></div>
        </div>
    </div>
</div>
复制代码
.fish{
   width: 60px;
   height: 30px;
   position:absolute;
   z-index:99;
}

.fish.left .fish-body{
    transform: scaleX(-1);
}
.fish.fish-type1 .fish-body{
    --main-skin:rgb(230,136,72);
}
.fish.fish-type2 .fish-body{
    --main-skin:rgb(230, 90, 72);
}
.fish.fish-type3 .fish-body{
    --main-skin:rgb(72, 127, 230);
}
.fish.fish-type4 .fish-body{
    --main-skin:rgb(241, 207, 94);
}
.fish.fish-type5 .fish-body{
    --main-skin:rgb(82, 151, 100);
}
.fish.fish-type6 .fish-body{
    --main-skin: rgb(255, 117, 117);
}
.fish-main{
    transition: .3s all;
}
.fish-body{
    position: relative;
    margin-left: 6px;
    width: 50px;
    height: 30px;
    border-radius: 50% 50%;
    border-bottom:1px solid rgba(0, 0, 0, .12);
    border-top:1px solid rgba(0, 0, 0, .06);
    background-color: var(--main-skin);
    transition: 1s all;
    transform-origin: 50% 50%;
}
.fish-body::before {
    content: '';
    display: block;
    position: absolute;
    left: -11px;
    width: 0;
    height: 0;
    border-left: solid 25px var(--main-skin);
    border-top: solid 15px transparent;
    border-bottom: solid 15px transparent;
    animation: move2 .24s linear infinite;
}
.fish-body::after {
    content: '';
    display: block;
    position: absolute;
    top: 8px;
    left: 34px;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background-color: black;
    box-shadow: 0px 0px 0 2px white;
}
.fish-fins{
    width: 0;
    height: 0;
    border-left: solid 6px var(--main-skin);
    border-top: solid 3px transparent;
    border-bottom: solid 3px transparent;
    position: absolute;
    top: 17px;
    left: 20px;
    filter: brightness(5.5);
    opacity: .1;
    animation: move .24s linear infinite;
    transform-origin: 100% 100%;
}
@keyframes move{
    0%{
        opacity: .1;
        transform: scaleX(1);
    }

    50%{
        opacity: .15;
        transform: scaleX(1.3);
    }

    100%{
        opacity: .1;
        transform: scaleX(1) ;
    }
}
@keyframes move2{
    0%{
        opacity: .9;
        transform: scaleX(1);
    }

    50%{
        opacity: 1;
        transform: scaleX(1.3);
    }

    100%{
        opacity: .9;
        transform: scaleX(1) ;
    }
}
复制代码

El pez pequeño que dibujamos se compone de cuatro partes: cuerpo de pez + ojo de pez + aleta pectoral + cola de pez. El cuerpo del pez es un rectángulo, el ojo de pez es un círculo y es muy simple de dibujar, mientras que la aleta pectoral y el pez la cola son triangulos, pasamos el borde Para hacer el dibujo, en la parte de la aleta pectoral, aumentamos una parte del brillo mediante el metodo filter:brightness para hacerlo mas realista. Además, las aletas pectorales y la cola de pez también pueden balancearse.Aquí, usamos la animación para hacer una animación de acercamiento y alejamiento, y luego controlamos el punto base a través del origen de transformación para lograr el efecto de balanceo. Además, considerando que el pez debe tener direcciones izquierda y derecha, usamos scaleX(-1) para voltear el pez.

WeChat captura de pantalla_20220404213434.png

Burbujas y Comida

<div class="bubble" :key="item.index" v-for="item in bubbleList" :style="{'left':item.x +'px','top':item.y+'px'}">
	<div class="bubble-body"></div>
</div>
<div class="food" v-for="item in foodList" :key="item.index" :style="{'left':item.x +'px','top':item.y+'px'}">
	<div class="food-body"></div>
</div>
复制代码
.bubble{
    width: 5px;
    height: 5px;
    position: absolute;
    animation: up 5s linear;
    animation-fill-mode: forwards;
}
.bubble-body{
    width: 5px;
    height: 5px;
    border:1px solid rgb(255,255,255);
    border-radius: 50%;
    position:absolute;
    left: 60px;
    top: 10px;
    opacity: 1;
    animation: sway 3s linear infinite;
}
.food{
   width: 10px;
   height: 7px;
   position: absolute;
   opacity: 1;
}
.food-body{
   position: absolute;
   width: 10px;
   height: 7px;
   border-radius: 45% 42%;
   background: rgb(82, 57, 43);
   animation: sway 3s linear infinite;
}
@keyframes up{
    0%{
        opacity: 1;
        transform: translateY(0);
    }
    100%{
        opacity: 0;
        transform: translateY(-600px);
    }
}
@keyframes sway{
    0%,20%,40%,60%,80%,100%{
        transform: translateX(0px)  rotate(0);
    }
    10%,30%,50%,70%,90%{
        transform: translateX(-10px)  rotate(30deg);
    }
}
复制代码

El dibujo de burbujas y comida es muy simple, uno es un círculo hueco y el otro es un rectángulo redondeado. Lo único que hay que explicar es que agregué una animación que se balancea hacia la izquierda y hacia la derecha, lo que hará que el mundo submarino sea más realista. . Además, hay una animación hacia arriba.Al escribir la lógica más tarde, hará que los peces salgan burbujas en diferentes ciclos y continúen flotando hacia arriba.

WeChat captura de pantalla_20220404213522.png

Lógica de piscicultura

 new Vue({
    el:".main",
    data:{
      fishNum:10,    // 生成鱼的数量
      fishList:[],   // 小鱼数组
      bubbleList:[], // 气泡数组
      foodList:[]    // 食物数组
    },
    mounted() {
      this.init();
    },
    methods: {
      init(){
        // 初始化事件
        this.width = window.innerWidth;
        this.height = window.innerHeight;
        for (let i = 0; i < this.fishNum; i++) {;
           let fish = this.addFish(i);
           this.fishList.push(fish)
        } 
        this.move();
        this.foodMove();
        this.throw();
        window.onresize = () =>{
          this.width = window.innerWidth;
          this.height = window.innerHeight;
         }
      },
      addFish(i){
        // 随机生成鱼的参数               
        return {
          index:`fish_${i}`,
          x: this.random(0,this.width-60),
          y: this.random(15,this.height-30),
          direction:(this.random(0,1)>0.5)?1:-1,
          type: 'fish-type'+~~(this.random(1,6)),
          speed:this.random(1,3),
          bTime:this.random(1,3)*100,
          bMax:this.random(3,10)*100,
          sy:Math.random(0,10),
          level:~~(this.random(0,2))
        }
      },
      move() {
          // 鱼群移动
      },
      addBubble(fish){
       	  // 追加气泡
            const {index,x,y} = fish;
            for (let i = 0; i < this.bubbleList.length; i++) {
              if(this.bubbleList[i].index == index){
                this.bubbleList.splice(i,1);
              }
            }
            this.bubbleList.push({x, y, index });
      },  
      throw(){
          // 投喂
            this.$refs.pool.addEventListener("click",e=>{
              let food = {
                x:e.layerX,
                y:e.layerY
              }
              let index = this.foodList.push(food);                  
            })
      },
      foodMove(){
          // 投喂食物不断下沉
             window.requestAnimationFrame(()=>{
              this.foodList.forEach((food,index)=>{
                food.y++;
                if(food.y>this.height){
                  this.foodList.splice(index,1);
                }
              })
              this.foodMove();
            });
      },
      random(min,max){
        return min + Math.random()*max
      }
})
复制代码

Al principio, generamos los parámetros de diferentes peces, como posición, dirección, velocidad, tamaño, etc., y luego los agregamos a la matriz fishList para controlarlos. Tenga en cuenta aquí que las coordenadas de la comida, los peces pequeños y las burbujas se cambian cuando el estilo de vue se ha obligado a traducir.

método addBubble: lo usaremos más adelante cuando escribamos la lógica de dibujar continuamente peces nadando (es decir, el método de movimiento).Aquí, las coordenadas y el objeto pez que escupe burbujas se agregan primero a la matriz bubbleList, porque el up La animación se acaba de escribir con css, y la animación de la burbuja es una sección. Desaparecerá después de un tiempo. Cuando el pez escupa la burbuja la próxima vez, se eliminará de la matriz bubbleList y se agregará nuevamente, y se podrá reproducir nuevamente.

Método de lanzamiento: registre un evento de clic y envíe las coordenadas de un alimento a la matriz foodList para cada clic.

Método foodMove: dibuja continuamente la animación de la comida cayendo y juzga que si se hunde hasta el fondo, deja que desaparezca.

function move() {
      window.requestAnimationFrame(() => {    
        this.fishList.forEach( fish => {
          
          // 到达临界值时吐气泡
          if (++fish.bTime > fish.bMax) {
            fish.bTime = 0;
            this.addBubble(fish);
          }
            
          // 找到最近的食物
          if (this.foodList.length > 0) {
            let foodIndex = 0;
            if (this.foodList.length > 1) {
              for (let i = 0, sub = null; i < this.foodList.length; i++) {
                let num = Math.abs(this.foodList[i].x - fish.x);
                if (sub == null) sub = num;
                if (num < sub) {
                  sub = num;
                  foodIndex = i;
                }
              }
            }
            
            // 根据最近的食物找到改变与的方向
            let food = this.foodList[foodIndex];
            let dx = food.x - fish.x;
            let dy = food.y - fish.y;
            if (dx >= 0) {
              fish.direction = 1;
            } else {
              fish.direction = -1;
            }

            // 计算方向改变鱼的移动,如果触碰到食物则升级
            let angle = Math.atan2(dy, dx);
            if (dx < 10 && dx > -10 && dy < 10 && dy > -10) {
              fish.level++; // 升级长胖
              this.foodList.splice(foodIndex, 1);
              fish.direction = Math.random() > 0.5 ? 1 : -1;
            } else {
              let vx = fish.speed * 1.2 * Math.cos(angle);
              let vy = fish.speed * 1.2 * Math.sin(angle);
              fish.x += vx;
              fish.y += vy;
            }
          } else {
            fish.x += fish.speed * fish.direction;
            fish.sy += 0.01;
            fish.y += Math.cos(fish.sy) * 2;
          }
            
		  // 边界判断
          if (fish.x < -60) {
            fish.x = -60;
            fish.direction *= -1;
            fish.speed = this.random(1, 3);
          }
          if (fish.x > this.width + 30) {
            fish.x = this.width + 30;
            fish.direction *= -1;
            fish.speed = this.random(1, 3);
          }
          if (fish.y < 0) {
            fish.y = 0;
          }
          if (fish.y > this.height - 30) {
            fish.y = this.height - 30;
          }
        });

        this.move();
   });
}
复制代码

Todavía hay mucha lógica de natación de peces. Lo que hace principalmente cada párrafo está escrito en los comentarios. La lógica de natación real de los peces es calcular la diferencia entre el punto objetivo y las coordenadas actuales del pez (es decir, dx y dy), y luego let angle = Math.atan2(dy, dx)calcule por Angle out, para obtener aceleración en diferentes direcciones (es decir, vx y vy), cambiando así las coordenadas actuales del pez.

WeChat captura de pantalla_20220404213659.png

Epílogo

El juego de piscicultura css se ha completado aquí, ¿cómo te sientes? Quizás descubrió que si hay demasiados peces, los nodos de redibujado ocuparán mucha memoria. Por supuesto, puede intentar usar canvas para dibujar, que también es una buena opción. La lógica central de js es aproximadamente la mismo.

PD: Este trabajo es un pequeño trabajo para entretenimiento personal en los primeros días. Puede ver el código fuente en codepen. Si los recién llegados intentan usar esta práctica, parece que tendrá algún efecto en su mejora de css o js. La piscicultura en sí también es una cuestión de autocultivo. Solo practicando sin prisas puedes sentar una base sólida. Trabajemos duro juntos ~

Supongo que te gusta

Origin juejin.im/post/7085138867560382494
Recomendado
Clasificación