变换......无休止地添加

时间:2017-05-01 17:40:54

标签: javascript css animation game-physics css-transforms

我正在使用DOM over canvas在CSS和JS中创建一个类似于小行星的简单游戏,用于实验目的。

我的代码在这个例子中非常小,可以很容易地看到下面发生了什么。最终目标:让箭头键平滑地旋转并在窗口周围转换太空船,而不会产生无限量的变换。 我想我90%

使用箭头键控制下面的代码段。

@import url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css');

@media (min-width: 768px) {
  .carousel .item {
    display: block;
    float: left;
    margin: 12px;
  }
  .carousel-control, 
  .carousel-indicators {
    display: none;
  }
}

/* Make the images wide and responsive. */
.carousel-inner img {
  height: auto;
  width: 100%;
}

/* Decorations */  
.carousel {
  margin-bottom: 20px;
  margin-top: 20px;
}
<div class="container">

  <div id="myCarousel" class="carousel slide" data-ride="carousel">
    <div class="carousel-inner" role="listbox">
      <div class="item active"><img src="https://placehold.it/100x100/fc3/fff/?text=1" alt=""></div>
      <div class="item"><img src="https://placehold.it/100x100/693/fff/?text=2" alt=""></div>
      <div class="item"><img src="https://placehold.it/100x100/369/fff/?text=3" alt=""></div>
      <div class="item"><img src="https://placehold.it/100x100/f63/fff/?text=4" alt=""></div>
      <div class="item"><img src="https://placehold.it/100x100/936/fff/?text=5" alt=""></div>
    </div>
    <a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev"><span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span><span class="sr-only">Previous</span></a>
    <a class="right carousel-control" href="#myCarousel" role="button" data-slide="next"><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span><span class="sr-only">Next</span></a>
  </div>

</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
'use strict';

function defineDistances() {
  var distance = {};

  distance.up = -1;
  distance.right = 1;
  distance.down = 1;
  distance.left = -1;
      
  return distance;
}

function defineKeys() {
  var keys = {};

  keys.up = 38;
  keys.right = 39;
  keys.down = 40;
  keys.left = 37;
      
  return keys;
}

function checkKeys( e ) {
  var triBx = document.getElementById( 'v-wrp' ),
      keys = defineKeys(),
      distance = defineDistances();

  switch( e.keyCode ) {
    case keys.up:
      triBx.style.transform += 'translateY(' + distance.up + 'px)';
      break;
    case keys.right:
      triBx.style.transform += 'rotate(' + distance.right + 'deg)';
      break;
    case keys.down:
      triBx.style.transform += 'translateY(' + distance.down + 'px)';
      break;
    case keys.left:
      triBx.style.transform += 'rotate(' + distance.left + 'deg)';
      break;
  }
}

function detectMovement( e ) {
  setInterval ( 
    function() {
      checkKeys( e );
    },
    1000/24
  );  
}

function start() {
  window.addEventListener( 'keydown', detectMovement );
  preventBrowserWindowScroll()
}

start();

如果您检查浏览器中的@import url( "https://fonts.googleapis.com/css?family=Nunito" ); html { display: flex; justify-content: center; align-items: center; height: 100%; font-family: "Nunito", sans-serif; font-size: 2rem; } .v { display: block; transform: rotate( 180deg ); }元素,您可以看到转换无休止地添加。

enter image description here

我使用<div id="v-wrp" class="v-wrp"> <b class="v">V</b> </div> <script> function preventBrowserWindowScroll() { window.addEventListener( 'keydown', function( e ) { // space and arrow keys if([32, 37, 38, 39, 40].indexOf( e.keyCode ) > -1 ) { e.preventDefault(); } }, false ) } </script>添加转换的原因是为了避免此问题:Reset CSS transform origin after translation / rotation

v-wrp在移动时不随元素移动,导致不良效果,除非除了之前的变换之外还添加了所有变换......)

那么我该如何克服这些挑战呢?我怀疑这个片段是如此不稳定,因为添加了无限的变换。如何在没有所有内存丢失/紊乱/ bugginess /无限变换的情况下使其工作方式与现在类似?

编辑:另一个主要问题是按下琴键后船舶将如何连续朝相同方向行驶,如果按下正确的琴键,即使是圆形的模式也是如此。我希望它像空间一样漂移,但一旦钥匙松开就不能转动。当它“漂浮”时,轨迹应保持笔直。我做错了什么?

3 个答案:

答案 0 :(得分:3)

我做了一个快速回答 - 可能有一些方面可以顺利完成,但你会得到这个想法:(ES6代码)

&#13;
&#13;
'use strict'

class Ship {
    constructor (elem) {
        this.posX = 0;
        this.posY = 0;
        this.deg = 0;
        this.rad = 0;
        this.speed = 0;
    }

    update (event) {
      switch( event.key ) {
        case "ArrowUp":
          this.speed += 5;
          break;
        case "ArrowDown":
          this.speed -= 5;
          if (this.speed < 0) this.speed = 0;
          break;
        case "ArrowRight":
          this.deg += 3;
          break;
        case "ArrowLeft":
          this.deg -= 3;
          break;
      }
          this.rad = (this.deg + 90) * Math.PI / 180;
    }
    move () {
      this.posX += this.speed * Math.cos(this.rad);
      this.posY += this.speed * Math.sin(this.rad);
      if (this.speed > 0) {
          this.speed -= 0.1;
      }
      if (this.elem == undefined) {
        this.elem = document.getElementById('ship');
      }
      var translation = 'translate(' + this.posX +'px, ' + this.posY + 'px) ';
      var rotation = 'rotate(' + this.deg + 'deg)';
      this.elem.style.transform = translation + rotation;
                     
    }
}


var ship = new Ship

function update( e ) {
  ship.update(e);
  return false;
}

function start() {
  window.addEventListener( 'keydown', update );
  setInterval ( 
    function() {
      ship.move();
    },
    1000 / 24
  );  
}

start();
&#13;
#ship {
  position: absolute;
  left: 50%;
  top: 50%;
}
&#13;
<div id="ship">V</div>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

我认为问题是detectMovement在具有相同事件e的无限循环中一次又一次地调用checkKeys。

我尝试为keyup,keydown,keyleft和keyright添加监听器,这样只有在按下这些键时才会调用checkkeys。

如果我理解错误,请发表评论

'use strict';

function defineDistances() {
  var distance = {};

  distance.up = -1;
  distance.right = 1;
  distance.down = 1;
  distance.left = -1;
      
  return distance;
}

function defineKeys() {
  var keys = {};

  keys.up = 38;
  keys.right = 39;
  keys.down = 40;
  keys.left = 37;
      
  return keys;
}

function checkKeys( e ) {
e.preventDefault();
  var triBx = document.getElementById( 'v-wrp' ),
      keys = defineKeys(),
      distance = defineDistances();

  switch( e.keyCode ) {
    case keys.up:
      triBx.style.transform += 'translateY(' + distance.up + 'px)';
      break;
    case keys.right:
      triBx.style.transform += 'rotate(' + distance.right + 'deg)';
      break;
    case keys.down:
      triBx.style.transform += 'translateY(' + distance.down + 'px)';
      break;
    case keys.left:
      triBx.style.transform += 'rotate(' + distance.left + 'deg)';
      break;
  }
}

function detectMovement( e ) {
  setInterval ( 
    function() {
      checkKeys( e );
    },
    1000/24
  );  
}

function start() {
  window.addEventListener( 'keydown', checkKeys );
  window.addEventListener( 'keyup', checkKeys );
  window.addEventListener( 'keyright', checkKeys );
  window.addEventListener( 'keyleft', checkKeys );
}

start();
@import url( "https://fonts.googleapis.com/css?family=Nunito" );

html {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  font-family: "Nunito", sans-serif;
  font-size: 2rem;
}

.v {
  display: block;
  transform: rotate( 180deg );
}
<div id="v-wrp" class="v-wrp">
  <b class="v">V</b>
</div>

<script>
function preventBrowserWindowScroll() {
  window.addEventListener( 'keydown', function( e ) {
    // space and arrow keys
    if([32, 37, 38, 39, 40].indexOf( e.keyCode ) > -1 ) {
      e.preventDefault();
    }
  }, false )
}
</script>

答案 2 :(得分:1)

有关以下答案的详情和演示,请参阅https://stackoverflow.com/a/43744006/3877726

使用CSS转换:矩阵函数

如果给定对象位置,缩放和旋转,设置变换的最快方法是将其作为单个矩阵element.style.transform = "matrix(a,b,c,d,e,f)";

6个值表示X轴(a,b),Y轴(c,d)和本地原点(e,f)的方向和比例

由于大多数时候你不想歪斜并且刻度是均匀的(x和y缩放相同),因此创建和设置变换的功能很快。你要做的就是通过位置,比例和旋转。

const setElementTransform = (function(){
    const matrix = [1,0,0,1,0,0]; // predefine the array (helps ease the GC load
    const m = matrix; // alias for code readability.
    return function(element, x, y, scale, rotation);
        m[3] = m[0] = Math.cos(rotation) * scale;     // set rotation and scale
        m[2] = -(m[1] = Math.sin(rotation) * scale);  // set rotation and scale
        m[4] = x;
        m[5] = y;
        element.style.transform = `matrix(${m.join(",")})`;
    }
}());

请勿使用已折旧的keyboardEvent.keyCode

不是使用旧的(和模糊的键值)keyCode属性来读取键,而应使用code属性,该属性具有表示哪个键已关闭或向上的字符串。

const keys = {
    ArrowLeft : false,  // add only the named keys you want to listen to.
    ArrowRight: false,  
    ArrowUp   : false,  
    ArrowDown : false,  
    stopKeyListener : (function(){  // adds a listener and returns function to stop key listener if needed.
        function keyEvent(e){
            if(keys[e.code] !== undefined){ // is the key on the named list
                keys[e.code] = e.type === "keydown"; // set true if down else false
                e.preventDefault(); // prevent the default Browser action for this key.
        }
        addEventListener("keydown",keyEvent);
        addEventListener("keyup",keyEvent);
        return function(){
            removeEventListener("keydown",keyEvent);
            removeEventListener("keyup",keyEvent);
        }
    }()) //
}

现在,您可以随时使用if(keys.ArrowLeft){

检查密钥是否已关闭

定期更新DOM?使用requestAnimationFrame

如果您定期对DOM进行许多更改,则应使用requestAnimationFrame,它会告诉浏览器您的意图,并会使回调中的所有DOM更改与显示硬件和DOM同步拥有合成和渲染。

requestAnimationFrame(mainLoop);  // will start the animation once code below has been parse and executed.
var player = {  // the player
    x : 0,
    y : 0,
    scale : 1,
    rotate : 0,
    speed : 0,
    element : document.getElementById("thePlayer")
}
function mainLoop(time){ // requestAnimationFrame adds the time as the first argument for the callback
     if(keys.ArrowLeft){ player.rotate -= 1 }
     if(keys.ArrowRight){ player.rotate += 1 }
     if(keys.ArrowUp){ player.speed  += 1 }
     if(keys.ArrowRight){ player.speed -= 1 }
     player.x += Math.cos(player.rotate) * player.speed;
     player.y += Math.sin(player.rotate) * player.speed;
     setElementTransform(
         player.element,
         player.x, player.y,
         player.scale,
         player.rotate
     );
     requestAnimationFrame(mainLoop);
}

对于演示https://stackoverflow.com/a/43744006/3877726(与答案顶部相同的链接)