【问题标题】:The same function called twice with different parameters but only last is executed同一个函数用不同的参数调用了两次,但只执行了最后一次
【发布时间】:2023-03-15 09:09:01
【问题描述】:

我有 2 个需要制作动画的 div:

<div class="d"></div>
<div class="p"></div>

第一个 div 的宽度应该变成 70%,第二个 div 的宽度应该变成 30%。但是,当我尝试为一个又一个 div 设置动画时,首先调用 70% 的函数,然后调用 30% 的函数,它们的宽度都变为 30%。

Javascript 代码:

Anim({
    target: document.getElementsByClassName('d')[0],
    drawFunc: (progress, element) => {
        element.style.width = (progress * 70) + '%';
    }
});

Anim({
    target: document.getElementsByClassName('p')[0],
    drawFunc: (progress, element) => {
        element.style.width = (progress * 30) + '%';
    }
});

我不明白为什么会发生这种情况以及如何使这两个功能正常工作。

如果需要,编码 sn-p:

(() => {
  "use strict";

  const init = (params) => {
    const start = performance.now();
    const element = params.target || null;

    requestAnimationFrame(function animate(time) {
      let timeFraction = (time - start) / params.duration;

      if (timeFraction > 1) {
        timeFraction = 1;
      }

      const progress = params.timingFunc(timeFraction, params.timingArg);

      params.drawFunc(progress, element);

      if (timeFraction < 1) {
        requestAnimationFrame(animate);
      }

      if (params.callback) {
        if (timeFraction >= 1) {
          params.callback();
        }
      }
    });
  };
  const timingFunctions = {
    linear: (timeFraction) => {
      return timeFraction;
    }
  };
  const paces = {
    easeIn: (func) => {
      return timingFunctions[func];
    }
  };
  const defaultParams = {
    duration: 1000,
    timingFunc: paces.easeIn('linear'),
    timingArg: null,
    delay: null,
    callback: null
  };
  const makeParams = (def, add) => {
    let params = def;

    if (add) {
      for (let i in add) {
        if (Object.prototype.hasOwnProperty.call(add, i)) {
          params[i] = add[i];
        }
      }
    }

    return params;
  };

  function Anim(paramArgs) {
    const params = makeParams(defaultParams, paramArgs);

    if ('timingFunc' in paramArgs) {
      params.timingFunc = (typeof paramArgs.timingFunc === 'function') ? paramArgs.timingFunc : paces[paramArgs.timingFunc.pace](paramArgs.timingFunc.func);
    }

    if (!params.delay) {
      init(params);
    } else {
      setTimeout(() => {
        init(params);
      }, params.delay);
    }
  }

  window.Anim = Anim;
})();

Anim({
  target: document.getElementsByClassName('d')[0],
  drawFunc: (progress, element) => {
    element.style.width = (progress * 70) + '%';
  }
});

Anim({
  target: document.getElementsByClassName('p')[0],
  drawFunc: (progress, element) => {
    element.style.width = (progress * 30) + '%';
  }
});
.d, .p {
    background-color: red;
    height: 50px;
    width: 0;
    margin-top: 10px;
  }
<div class="d"></div>
<div class="p"></div>

【问题讨论】:

  • console.log("p1:"+progress); 会给你答案 ;)
  • if (Object.prototype.hasOwnProperty.call(add, i)) { 这里发生了什么?这里调用了hasOwnProperty 方法吗?为什么不直接使用 is 呢?
  • @messerbill 是的,这里调用了 hasOwnProperty 方法。我需要 IE8 支持,直接使用在那里不起作用。
  • :'D 好旧的 IE .....这很清楚....如果你看到某种看起来很奇怪的代码,请考虑一下......嗯,一定是 IE修复:D 告诉您的客户是时候升级他们的浏览器了
  • @messerbill 这个链接很好地解释了为什么我写 Object.prototype.hasOwnProperty.call 而不是直接使用:stackoverflow.com/questions/12017693/…

标签: javascript


【解决方案1】:

问题是两个Anim 调用具有相同的params 对象。两个params 对象具有完全相同的回调drawFunc

为什么?因为在makeParams你正在这样做:

let params = def;

然后你分配给params,这反过来又改变了原来的defaultParams(这里别名为def)。当第二个函数调用Anim 时,第二个调用的回调drawFunc 被分配给defaultParams 对象。由于所有params 对象基本上都是对defaultParams 的引用,因此它们也会被更改,并且最后一次调用Anim 的回调被分配给所有这些对象。

要解决此问题,只需使用 Object.assign 克隆 def

let params = Object.assign({}, def);

旁注:target 属性在 params 对象中也发生了更改,但在它更改之前,它被分配给 init 内的一个新变量:

const element = params.target || null;

因此,即使它在 params 对象中发生变化,您也不会真正注意到,因为所有后续代码都使用变量 element 而不是 params.target

工作代码:

(() => {
  "use strict";

  const init = (params) => {
    const start = performance.now();
    const element = params.target || null;

    requestAnimationFrame(function animate(time) {
      let timeFraction = (time - start) / params.duration;

      if (timeFraction > 1) {
        timeFraction = 1;
      }

      const progress = params.timingFunc(timeFraction, params.timingArg);

      params.drawFunc(progress, element);

      if (timeFraction < 1) {
        requestAnimationFrame(animate);
      }

      if (params.callback) {
        if (timeFraction >= 1) {
          params.callback();
        }
      }
    });
  };
  const timingFunctions = {
    linear: (timeFraction) => {
      return timeFraction;
    }
  };
  const paces = {
    easeIn: (func) => {
      return timingFunctions[func];
    }
  };
  const defaultParams = {
    duration: 1000,
    timingFunc: paces.easeIn('linear'),
    timingArg: null,
    delay: null,
    callback: null
  };
  const makeParams = (def, add) => {
    let params = Object.assign({}, def);

    if (add) {
      for (let i in add) {
        if (Object.prototype.hasOwnProperty.call(add, i)) {
          params[i] = add[i];
        }
      }
    }

    return params;
  };

  function Anim(paramArgs) {
    const params = makeParams(defaultParams, paramArgs);

    if ('timingFunc' in paramArgs) {
      params.timingFunc = (typeof paramArgs.timingFunc === 'function') ? paramArgs.timingFunc : paces[paramArgs.timingFunc.pace](paramArgs.timingFunc.func);
    }

    if (!params.delay) {
      init(params);
    } else {
      setTimeout(() => {
        init(params);
      }, params.delay);
    }
  }

  window.Anim = Anim;
})();

Anim({
  target: document.getElementsByClassName('d')[0],
  drawFunc: (progress, element) => {
    element.style.width = (progress * 70) + '%';
  }
});

Anim({
  target: document.getElementsByClassName('p')[0],
  drawFunc: (progress, element) => {
    element.style.width = (progress * 30) + '%';
  }
});
.d, .p {
    background-color: red;
    height: 50px;
    width: 0;
    margin-top: 10px;
  }
<div class="d"></div>
<div class="p"></div>

相关问题: How do I correctly clone a JavaScript object?

【讨论】:

  • 谢谢。你拯救了我的一天。
  • @alexDev 一个小提示,用Object.assign代替Object.create,我头疼,所以我的想法不清楚,对不起!查看更新的答案! Object.create,虽然有效,但适用于原型继承而不是克隆。
猜你喜欢
  • 2016-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-22
  • 1970-01-01
  • 2021-12-19
  • 2021-10-15
相关资源
最近更新 更多