代码之家  ›  专栏  ›  技术社区  ›  Ayus Chatterjee

如何在此处正确过滤触摸事件的坐标

  •  0
  • Ayus Chatterjee  · 技术社区  · 10 月前

    我有一个简单蛋糕的html、css和js代码,我想在蛋糕上放一些蜡烛。但我希望蜡烛只放在标有css属性的蛋糕糖衣上 .icing 使用了一种算法来限制向蛋糕的其他层添加蜡烛,但仍然可以在蛋糕的结冰层外添加蜡烛,特别是滴液。我怎样才能让蜡烛只放在蛋糕的糖衣上(蛋糕顶部的圆盘)

    const cake = document.querySelector(".cake");
     
    let candles = [];
    
    function addCandle(left, top) {
    
      const icing = document.querySelector(".icing");
      const icingRect = icing.getBoundingClientRect();
      
      const icingCenterX = icingRect.left + icingRect.width / 2;
      const icingCenterY = icingRect.top + icingRect.height / 2;
      const icingRadius = icingRect.width / 2;
      
      const cakeRect = cake.getBoundingClientRect();
      const clickX = event.clientX - cakeRect.left;
      const clickY = event.clientY - cakeRect.top;
      
      const distance = Math.sqrt(
        Math.pow(clickX - (icingCenterX - cakeRect.left), 2) + 
        Math.pow(clickY - (icingCenterY - cakeRect.top), 2)
      );
    
      if (distance <= icingRadius && clickY >= icingRect.top - cakeRect.top && clickY <= icingRect.bottom - cakeRect.top) {
        const candle = document.createElement("div");
        candle.className = "candle";
        candle.style.left = left + "px";
        candle.style.top = top + "px";
    
        const flame = document.createElement("div");
        flame.className = "flame";
        candle.appendChild(flame);
    
        cake.appendChild(candle);
        candles.push(candle);
      } else {
        console.log("Clicked outside of icing or in an invalid area, no candle added.");
      }
    }
    
      cake.addEventListener("click", function (event) {
    
      const activeCandles = candles.filter(
        (candle) => !candle.classList.contains("out")
      ).length;
    
      const rect = cake.getBoundingClientRect();
      const left = event.clientX - rect.left;
      const top = event.clientY - rect.top;
      addCandle(left, top);
      });
    .cake {
      position: absolute;
      width: 250px;
      height: 200px;
      top: 50%;
      left: 50%;
      margin-top: -70px;
      margin-left: -125px;
    }
    
    .plate {
      width: 270px;
      height: 110px;
      position: absolute;
      bottom: -10px;
      left: -10px;
      background-color: #ccc;
      border-radius: 50%;
      box-shadow: 0 2px 0 #b3b3b3, 0 4px 0 #b3b3b3, 0 5px 40px rgba(0, 0, 0, 0.5);
    }
    
    .cake > * {
      position: absolute;
    }
    
    .layer {
      position: absolute;
      display: block;
      width: 250px;
      height: 100px;
      border-radius: 50%;
      background-color: #553c13;
      box-shadow: 0 2px 0px #6a4b18, 0 4px 0px #33240b, 0 6px 0px #32230b, 0 8px 0px #31230b, 0 10px 0px #30220b, 0 12px 0px #2f220b, 0 14px 0px #2f210a, 0 16px 0px #2e200a, 0 18px 0px #2d200a, 0 20px 0px #2c1f0a, 0 22px 0px #2b1f0a, 0 24px 0px #2a1e09, 0 26px 0px #2a1d09, 0 28px 0px #291d09, 0 30px 0px #281c09;
    }
    
    .layer-top {
      top: 0px;
    }
    
    .layer-middle {
      top: 33px;
    }
    
    .layer-bottom {
      top: 66px;
    }
    
    .icing {
      top: 2px;
      left: 5px;
      background-color: #f0e4d0;
      width: 240px;
      height: 90px;
      border-radius: 50%;
    }
    .icing:before {
      content: "";
      position: absolute;
      top: 4px;
      right: 5px;
      bottom: 6px;
      left: 5px;
      background-color: #f4ebdc;
      box-shadow: 0 0 4px #f6efe3, 0 0 4px #f6efe3, 0 0 4px #f6efe3;
      border-radius: 50%;
      z-index: 1;
    }
    
    .drip {
     display: block;
      width: 50px;
      height: 60px;
      border-bottom-left-radius: 25px;
      border-bottom-right-radius: 25px;
      background-color: #f0e4d0; 
    }
    
    .drip1 {
      top: 53px;
      left: 5px;
      transform: skewY(15deg);
      height: 48px;
      width: 40px;
    }
    
    .drip2 {
      top: 69px;
      left: 181px;
      transform: skewY(-15deg);
    }
    
    .drip3 {
      top: 54px;
      left: 90px;
      width: 80px;
      border-bottom-left-radius: 40px;
      border-bottom-right-radius: 40px;
    }
    
    .candle {
      background-color: #7B020B;
      width: 12px;
      height: 35px;
      border-radius: 6px/3px;
      top: -20px;
      left: 50%;
      margin-left: -8px;
      z-index: 10;
    }
    .candle:before {
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      width: 12px;
      height: 6px;
      border-radius: 50%;
      background-color: #ad030f;
    }
    
    .candle.out .flame {
      display: none;
    }
    
    .flame {
      position: absolute;
      background-color: orange;
      width: 10px;
      height: 25px;
      border-radius: 8px 8px 8px 8px/20px 20px 8px 8px;
      top: -34px;
      left: 50%;
      margin-left: -7.5px;
      z-index: 10;
      box-shadow: 0 0 10px rgba(255, 165, 0, 0.5), 0 0 20px rgba(255, 165, 0, 0.5), 0 0 60px rgba(255, 165, 0, 0.5), 0 0 80px rgba(255, 165, 0, 0.5);
      transform-origin: 50% 90%;
      animation: flicker 1s ease-in-out alternate infinite;
    }
    <div class="cake">
          <div class="plate"></div>
          <div class="layer layer-bottom"></div>
          <div class="layer layer-middle"></div>
          <div class="layer layer-top"></div>
          <div class="icing"></div>
          <div class="drip drip1"></div>
          <div class="drip drip2"></div>
          <div class="drip drip3"></div>
    </div>

    我原以为蜡烛只能放在蛋糕的糖衣上,但它们也可以放在外面

    1 回复  |  直到 10 月前
        1
  •  1
  •   Diego D    10 月前

    我在这里对你的代码做了最少的更改:

    1. 监听点击事件处理程序,特别是通过 .icing 元素而非整体 .cake ;
    2. 更改 top 新添加的candle的css属性,只是 在将其推入dom后,减去其计算高度 以便其底面与您点击的位置相匹配;
    3. 添加了样式 border: solid 3px red; 对于 结冰 到 突出其周边;

    这样,它就非常接近预期的结果。实际上,我也试图改变 left 协调,使蜡烛的左下角与点击点相匹配。但这会让事情变得更糟。为了使其更加准确,您应该防止在单击的坐标离目标元素的边距太近的情况下发生事件(就蜡烛宽度的一半或考虑底部边距而言)。

    const cake = document.querySelector(".cake");
    
    let candles = [];
    
    function addCandle(left, top) {
    
      const icing = document.querySelector(".icing");
      const icingRect = icing.getBoundingClientRect();
    
      const icingCenterX = icingRect.left + icingRect.width / 2;
      const icingCenterY = icingRect.top + icingRect.height / 2;
      const icingRadius = icingRect.width / 2;
    
      const cakeRect = cake.getBoundingClientRect();
      const clickX = event.clientX - cakeRect.left;
      const clickY = event.clientY - cakeRect.top;
    
      const distance = Math.sqrt(
        Math.pow(clickX - (icingCenterX - cakeRect.left), 2) +
        Math.pow(clickY - (icingCenterY - cakeRect.top), 2)
      );
    
      if (distance <= icingRadius && clickY >= icingRect.top - cakeRect.top && clickY <= icingRect.bottom - cakeRect.top) {
        const candle = document.createElement("div");
        candle.className = "candle";
        candle.style.left = left + "px";
        candle.style.top = top + "px";
    
        const flame = document.createElement("div");
        flame.className = "flame";
        candle.appendChild(flame);
    
        cake.appendChild(candle);
    
        //changing the candle's top coord substracting its computed height after it was pushed into the dom
        candle.style.top = (parseInt(candle.style.top) - parseInt(window.getComputedStyle(candle).height)) + 'px';
        //candle.style.left = (parseInt(candle.style.left) + parseInt(window.getComputedStyle(candle).width)) + 'px';
    
        candles.push(candle);
      } else {
        console.log("Clicked outside of icing or in an invalid area, no candle added.");
      }
    }
    
    //listening to the click event on the .icing element only
    const icing = document.querySelector('.icing');
    icing.addEventListener("click", function(event) {
    
      const activeCandles = candles.filter(
        (candle) => !candle.classList.contains("out")
      ).length;
    
      const rect = cake.getBoundingClientRect();
      const left = event.clientX - rect.left;
      const top = event.clientY - rect.top;
      addCandle(left, top);
    });
    .cake {
      position: absolute;
      width: 250px;
      height: 200px;
      top: 50%;
      left: 50%;
      margin-top: -70px;
      margin-left: -125px;
    }
    
    .plate {
      width: 270px;
      height: 110px;
      position: absolute;
      bottom: -10px;
      left: -10px;
      background-color: #ccc;
      border-radius: 50%;
      box-shadow: 0 2px 0 #b3b3b3, 0 4px 0 #b3b3b3, 0 5px 40px rgba(0, 0, 0, 0.5);
    }
    
    .cake>* {
      position: absolute;
    }
    
    .layer {
      position: absolute;
      display: block;
      width: 250px;
      height: 100px;
      border-radius: 50%;
      background-color: #553c13;
      box-shadow: 0 2px 0px #6a4b18, 0 4px 0px #33240b, 0 6px 0px #32230b, 0 8px 0px #31230b, 0 10px 0px #30220b, 0 12px 0px #2f220b, 0 14px 0px #2f210a, 0 16px 0px #2e200a, 0 18px 0px #2d200a, 0 20px 0px #2c1f0a, 0 22px 0px #2b1f0a, 0 24px 0px #2a1e09, 0 26px 0px #2a1d09, 0 28px 0px #291d09, 0 30px 0px #281c09;
    }
    
    .layer-top {
      top: 0px;
    }
    
    .layer-middle {
      top: 33px;
    }
    
    .layer-bottom {
      top: 66px;
    }
    
    .icing {
      top: 2px;
      left: 5px;
      background-color: #f0e4d0;
      width: 240px;
      height: 90px;
      border-radius: 50%;
    }
    
    .icing:before {
      content: "";
      position: absolute;
      top: 4px;
      right: 5px;
      bottom: 6px;
      left: 5px;
      background-color: #f4ebdc;
      box-shadow: 0 0 4px #f6efe3, 0 0 4px #f6efe3, 0 0 4px #f6efe3;
      border-radius: 50%;
      z-index: 1;
    }
    
    .drip {
      display: block;
      width: 50px;
      height: 60px;
      border-bottom-left-radius: 25px;
      border-bottom-right-radius: 25px;
      background-color: #f0e4d0;
    }
    
    .drip1 {
      top: 53px;
      left: 5px;
      transform: skewY(15deg);
      height: 48px;
      width: 40px;
    }
    
    .drip2 {
      top: 69px;
      left: 181px;
      transform: skewY(-15deg);
    }
    
    .drip3 {
      top: 54px;
      left: 90px;
      width: 80px;
      border-bottom-left-radius: 40px;
      border-bottom-right-radius: 40px;
    }
    
    .candle {  
      background-color: #7B020B;
      width: 12px;
      height: 35px;
      border-radius: 6px/3px;
      top: -20px;
      left: 50%;
      margin-left: -8px;
      z-index: 10;
    }
    
    .candle:before {
      content: "";
      position: absolute;
      top: 0;
      left: 0;
      width: 12px;
      height: 6px;
      border-radius: 50%;
      background-color: #ad030f;
    }
    
    .candle.out .flame {
      display: none;
    }
    
    .flame {
      position: absolute;
      background-color: orange;
      width: 10px;
      height: 25px;
      border-radius: 8px 8px 8px 8px/20px 20px 8px 8px;
      top: -34px;
      left: 50%;
      margin-left: -7.5px;
      z-index: 10;
      box-shadow: 0 0 10px rgba(255, 165, 0, 0.5), 0 0 20px rgba(255, 165, 0, 0.5), 0 0 60px rgba(255, 165, 0, 0.5), 0 0 80px rgba(255, 165, 0, 0.5);
      transform-origin: 50% 90%;
      animation: flicker 1s ease-in-out alternate infinite;
    }
    
    /*highlighting the .icing border*/
    .icing{
      border: solid 3px red;
    }
    
    .candle{
      border: solid 3px green;
    }
    <div class="cake">
      <div class="plate"></div>
      <div class="layer layer-bottom"></div>
      <div class="layer layer-middle"></div>
      <div class="layer layer-top"></div>
      <div class="icing"></div>
      <div class="drip drip1"></div>
      <div class="drip drip2"></div>
      <div class="drip drip3"></div>
    </div>