变换被添加......无休止地 [英] Transforms are added...endlessly

查看:30
本文介绍了变换被添加......无休止地的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

在这个例子中,我的代码非常小,以便于查看下面发生了什么.最终目标:让箭头键平滑旋转并围绕窗口平移飞船,而不会产生无限量的变换. 我想我已经做到了 90%:

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

'use strict';函数定义距离(){var 距离 = {};距离.up = -1;距离.right = 1;距离.向下 = 1;距离.left = -1;返回距离;}函数定义键(){var 键 = {};键.up = 38;键.right = 39;键.down = 40;键.left = 37;返回键;}函数 checkKeys( e ) {var triBx = document.getElementById('v-wrp'),键 = 定义键(),距离=定义距离();开关(e.keyCode){案例keys.up:triBx.style.transform += 'translateY(' + distance.up + 'px)';休息;案例keys.right:triBx.style.transform += 'rotate(' + distance.right + 'deg)';休息;案例keys.down:triBx.style.transform += 'translateY(' + distance.down + 'px)';休息;案例keys.left:triBx.style.transform += 'rotate(' + distance.left + 'deg)';休息;}}函数检测运动( e ) {设置间隔(功能() {checkKeys(e);},1000/24);}函数开始(){window.addEventListener('keydown',detectMovement);防止浏览器窗口滚动()}start();

@import url( "https://fonts.googleapis.com/css?family=Nunito" );html {显示:弹性;对齐内容:居中;对齐项目:居中;高度:100%;字体系列:Nunito",无衬线;字体大小:2rem;}.v {显示:块;变换:旋转(180度);}

<b class="v">V</b>

<脚本>函数 preventBrowserWindowScroll() {window.addEventListener('keydown', function( e ) {//空格键和箭头键if([32, 37, 38, 39, 40].indexOf( e.keyCode ) > -1 ) {e.preventDefault();}}, 错误的 )}</script>

如果您检查浏览器中的 v-wrp 元素,您会看到变换被无限添加.

我使用 += 添加转换的原因是为了避免这个问题:平移/旋转后重置 CSS 变换原点

(transform-origin 在元素移动时不会随元素一起移动,从而导致不希望的效果,除非除了先前的转换之外添加所有转换...)

那么我该如何克服这些挑战呢?我怀疑由于添加了无休止的变换,因此该代码段如此断断续续.我如何才能像现在这样工作,而没有所有的记忆损失/断断续续/错误/无休止的转换?

另一个主要问题是,一旦按下按键,飞船将如何以相同的方向连续行驶,如果您按下正确的按键,甚至会以圆形模式运行.我希望它像在太空中一样漂移,但一旦松开钥匙就不会转动.轨迹在漂浮"时应该保持直线,我做错了什么?

解决方案

有关更多信息和以下答案的演示,请参阅 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 矩阵 = [1,0,0,1,0,0];//预定义数组(有助于减轻 GC 负载const m = 矩阵;//代码可读性的别名.返回函数(元素,x,y,比例,旋转);m[3] = m[0] = Math.cos(rotation) * scale;//设置旋转和缩放m[2] = -(m[1] = Math.sin(rotation) * scale);//设置旋转和缩放m[4] = x;m[5] = y;element.style.transform = `matrix(${m.join(",")})`;}}());


不要使用已经贬值的 keyboardEvent.keyCode.

与其使用旧的(和晦涩的键值)keyCode 属性来读取键,您应该使用 code 属性,该属性具有一个字符串,表示哪个键按下或

const 键 = {ArrowLeft : false,//只添加你想听的命名键.右箭头:假,箭头向上:假,向下箭头:假,stopKeyListener : (function(){//添加一个监听器并返回函数以在需要时停止键监听器.功能键事件(e){if(keys[e.code] !== undefined){//是命名列表上的键keys[e.code] = e.type === "keydown";//如果关闭则设置为真否则为假e.preventDefault();//阻止此键的默认浏览器操作.}addEventListener("keydown",keyEvent);addEventListener("keyup",keyEvent);返回函数(){removeEventListener("keydown",keyEvent);removeEventListener("keyup",keyEvent);}}())//}

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

检查某个键是否已关闭

定期更新 DOM?使用 requestAnimationFrame

如果您定期对 DOM 进行许多更改,则应使用 requestAnimationFrame 并且它会告诉浏览器您的意图,并将导致在回调中进行的所有 DOM 更改与显示硬件同步以及 DOM 自己的合成和渲染.

requestAnimationFrame(mainLoop);//一旦下面的代码被解析和执行,就会开始动画.var player = {//玩家x : 0,y : 0,规模:1,旋转:0,速度:0,元素:document.getElementById(thePlayer")}function mainLoop(time){//requestAnimationFrame 添加时间作为回调的第一个参数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;设置元素转换(玩家元素,球员.x, 球员.y,玩家规模,player.rotate);requestAnimationFrame(mainLoop);}

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

I'm creating a simple asteroids-like game in CSS and JS using the DOM over canvas for...experimentation purposes.

My code is pretty small in this example to make it easy to see what's going on below. The ultimate goal: Let arrow keys smoothly rotate and translate the spaceship around the window without creating an infinite amount of transforms. I think I'm 90% there:

Use the arrow keys to control the snippet below.

'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 );
}

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

If you inspect the v-wrp element in the browser you can see the transforms get added endlessly.

The reason I use += to add transforms is to avoid this problem: Reset CSS transform origin after translation / rotation

( The transform-origin doesn't move with the element as it moves, causing undesired effects unless all transforms are added in addition to the previous ones... )

So how do I overcome these challenges? I suspect the snippet is so choppy because of the endless transforms being added. How do I get this working similarly to the way it is now without all the memory loss/ choppiness/ bugginess / endless transforms?

Edit: Another major problem is how the ship will travel in the same directions continuously once the keys are pressed, even going in a circular like pattern if you hit the correct keys. I want it to drift like in space but not turn once the keys are let go. The trajectory should stay straight as it "floats" What am I doing wrong?.

解决方案

For more info and a demo of the answer below see https://stackoverflow.com/a/43744006/3877726


Using CSS transform : matrix function

If given a object position, scale and rotation the quickest way to set the transform is to do it as a single matrix element.style.transform = "matrix(a,b,c,d,e,f)";

The 6 values represent the direction and scale of the X axis (a,b), Y axis (c,d) , and the local origin (e,f)

As most of the time you don't want to skew and the scale is uniform (x and y scale the same) the function to create and set the transform is quick. All you do is pass the position, scale and rotation.

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(",")})`;
    }
}());


Don't use keyboardEvent.keyCode it has depreciated.

Rather than use the old (and obscure key values) keyCode property to read the keys you should use the code property that has a string representing which key is down or up.

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);
        }
    }()) //
}

Now at any time you can just check if a key is down with if(keys.ArrowLeft){


Updating the DOM regularly? use requestAnimationFrame

If you are making many changes to the DOM at regular intervals you should use requestAnimationFrame and it tells the browser your intention and will cause all DOM changes made from within the callback to sync with the display hardware and the DOM's own compositing and rendering.

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);
}
      

For a demo https://stackoverflow.com/a/43744006/3877726 (same link as at top of answer)

这篇关于变换被添加......无休止地的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆