CSS/JS:改变刷卡时的不透明度

时间:2022-09-04 09:08:28

I want to change the opacity of an element while swiping on it.

我想要改变一个元素的不透明度,同时在它上滑动。

I would like to achieve an animation similar to the one in the snippet, that is applied gradually depending on how much my finger/cursor has dragged the element while swiping.

我想要实现一个类似于代码片段中的动画,它是根据我的手指/游标在滑动时拖动元素的多少而逐渐应用的。

EDIT: The animation is the same as clearing a notification in Android

编辑:动画与清除Android中的通知是一样的

  • My first idea has been to handle the drag event and change the opacity depending on the position of the element and the width of the screen. Is this a good solution? Is there a better one, maybe CSS only?
  • 我的第一个想法是处理拖动事件,并根据元素的位置和屏幕的宽度来改变不透明度。这是一个好的解决方案吗?有没有更好的,也许只有CSS ?

I'm using ionic (my element is a ion-item), so anything related to ionic/angular1 could be good too.

我正在使用离子(我的元素是离子项),所以任何与离子/角弧1有关的东西也可以很好。

div.animated {
    width: 100px;
    height: 100px;
    background: red;
    position: absolute;
    top: 31px;
    animation: right 2s;
    animation-iteration-count: infinite;
    animation-direction: alternate;
    animation-timing-function: linear;
}

.back {
    width: 100px;
    height: 100px;
    border: 1px solid blue;
    position: fixed;
    top: 30px;
    left: 50px;
}

@keyframes right {
    0% {
       left: 0px;
       opacity: 0.1;
    }
    50% { opacity: 1;}
    100% {left: 100px;opacity:0.1}
}
The blue frame is the screen and the red square is the dragged element
<div class="animated"></div>
<div class="back"></div>

2 个解决方案

#1


2  

The very nice people over at google chrome devs run a show call SuperCharged the idea behind the show is to show you quick and simple ways to make web apps effects.

在谷歌chrome开发公司工作的很不错的人会运行一个名为supercharge的节目,这个节目背后的想法是向你展示快速、简单的方法来让web应用程序产生效果。

They did one episode (which is about an hour long) about swipeable cards, they did a quick 10-minute episode just to give you the basic idea.

他们做了一集(大约一小时)关于可刷卡的节目,他们做了一个10分钟的节目只是为了给你一个基本的概念。

To answer your question javascript is the only way to make it respond, CSS doesn't respond to user input or action.

要回答您的问题,javascript是使它响应的唯一方式,CSS不响应用户输入或操作。

Also, it is best to use transform, as opposed to left, when moving things across the screen. They explain the reasons why in great detail during the show but the quick reason is transform can use the GPU.

此外,当在屏幕上移动物体时,最好使用transform,而不是向左。他们在节目中详细解释了原因,但快速的原因是转换可以使用GPU。

Anyway here is a live demo of the code they made in the episode, give it a glance and see if it's what you're looking for. I'd recommending watching their videos anyway, you can learn a lot (I certainly did).

不管怎样,这是他们在这集里所做的代码的一个现场演示,看看它是不是你要找的。无论如何,我建议你看看他们的视频,你可以学到很多东西(我当然学到了)。

/**
 Copyright 2016 Google Inc. All rights reserved.
 Licensed under the Apache License, Version 2.0 (the "License");
 */

'use strict';

class Cards {
  constructor() {
    this.cards = Array.from(document.querySelectorAll('.card'));

    this.onStart = this.onStart.bind(this);
    this.onMove = this.onMove.bind(this);
    this.onEnd = this.onEnd.bind(this);
    this.update = this.update.bind(this);
    this.targetBCR = null;
    this.target = null;
    this.startX = 0;
    this.currentX = 0;
    this.screenX = 0;
    this.targetX = 0;
    this.draggingCard = false;

    this.addEventListeners();

    requestAnimationFrame(this.update);
  }

  addEventListeners() {
    document.addEventListener('touchstart', this.onStart);
    document.addEventListener('touchmove', this.onMove);
    document.addEventListener('touchend', this.onEnd);

    document.addEventListener('mousedown', this.onStart);
    document.addEventListener('mousemove', this.onMove);
    document.addEventListener('mouseup', this.onEnd);
  }

  onStart(evt) {
    if (this.target)
      return;

    if (!evt.target.classList.contains('card'))
      return;

    this.target = evt.target;
    this.targetBCR = this.target.getBoundingClientRect();

    this.startX = evt.pageX || evt.touches[0].pageX;
    this.currentX = this.startX;

    this.draggingCard = true;
    this.target.style.willChange = 'transform';

    evt.preventDefault();
  }

  onMove(evt) {
    if (!this.target)
      return;

    this.currentX = evt.pageX || evt.touches[0].pageX;
  }

  onEnd(evt) {
    if (!this.target)
      return;

    this.targetX = 0;
    let screenX = this.currentX - this.startX;
    if (Math.abs(screenX) > this.targetBCR.width * 0.35) {
      this.targetX = (screenX > 0) ? this.targetBCR.width : -this.targetBCR.width;
    }

    this.draggingCard = false;
  }

  update() {

    requestAnimationFrame(this.update);

    if (!this.target)
      return;

    if (this.draggingCard) {
      this.screenX = this.currentX - this.startX;
    } else {
      this.screenX += (this.targetX - this.screenX) / 4;
    }

    const normalizedDragDistance =
      (Math.abs(this.screenX) / this.targetBCR.width);
    const opacity = 1 - Math.pow(normalizedDragDistance, 3);

    this.target.style.transform = `translateX(${this.screenX}px)`;
    this.target.style.opacity = opacity;

    // User has finished dragging.
    if (this.draggingCard)
      return;

    const isNearlyAtStart = (Math.abs(this.screenX) < 0.1);
    const isNearlyInvisible = (opacity < 0.01);

    // If the card is nearly gone.
    if (isNearlyInvisible) {

      // Bail if there's no target or it's not attached to a parent anymore.
      if (!this.target || !this.target.parentNode)
        return;

      this.target.parentNode.removeChild(this.target);

      const targetIndex = this.cards.indexOf(this.target);
      this.cards.splice(targetIndex, 1);

      // Slide all the other cards.
      this.animateOtherCardsIntoPosition(targetIndex);

    } else if (isNearlyAtStart) {
      this.resetTarget();
    }
  }

  animateOtherCardsIntoPosition(startIndex) {
    // If removed card was the last one, there is nothing to animate. Remove target.
    if (startIndex === this.cards.length) {
      this.resetTarget();
      return;
    }

    const frames = [{
      transform: `translateY(${this.targetBCR.height + 20}px)`
    }, {
      transform: 'none'
    }];
    const options = {
      easing: 'cubic-bezier(0,0,0.31,1)',
      duration: 150
    };
    const onAnimationComplete = () => this.resetTarget();

    for (let i = startIndex; i < this.cards.length; i++) {
      const card = this.cards[i];

      // Move the card down then slide it up.
      card
        .animate(frames, options)
        .addEventListener('finish', onAnimationComplete);
    }
  }

  resetTarget() {
    if (!this.target)
      return;

    this.target.style.willChange = 'initial';
    this.target.style.transform = 'none';
    this.target = null;
  }
}

window.addEventListener('load', () => new Cards());
/**
 Copyright 2016 Google Inc. All rights reserved. 
 Licensed under the Apache License, Version 2.0 (the "License");
 */

html,
body {
  margin: 0;
  padding: 0;
  background: #FAFAFA;
  font-family: Arial;
  font-size: 30px;
  color: #333;
}
* {
  box-sizing: border-box;
}
.card-container {
  width: 100%;
  max-width: 450px;
  padding: 16px;
  margin: 0 auto;
}
.card {
  background: #FFF;
  border-radius: 3px;
  box-shadow: 0 3px 4px rgba(0, 0, 0, 0.3);
  margin: 20px 0;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: space-around;
  cursor: pointer;
}
<!-- 
https://github.com/GoogleChrome/ui-element-samples/tree/master/swipeable-cards

https://www.youtube.com/watch?v=rBSY7BOYRo4

/**
 *
 * Copyright 2016 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-->

<div class="card-container">
  <div class="card">Das Surma</div>
  <div class="card">Aerotwist</div>
  <div class="card">Kinlanimus Maximus</div>
  <div class="card">Addyoooooooooo</div>
  <div class="card">Gaunty McGaunty Gaunt</div>
  <div class="card">Jack Archibungle</div>
  <div class="card">Sam "The Dutts" Dutton</div>
</div>

#2


0  

Ansrew's is very useful. In Ionic it is easier to use onDrag and onRelease directives.

Ansrew是非常有用的。在Ionic中,更容易使用onDrag和onRelease指令。

<ion-item on-drag="onDrag($event)" on-release="onRelease($event)" /> 

And use these methods to style the ion-item:

并使用这些方法对离子项进行样式化:

function onDrag (e) {
    var element = e.currentTarget.childNodes[0];
    var screenW = element.offsetWidth;
    var threshold = screenW * 0.16;

    var delta = Math.abs(e.gesture.deltaX);

    if(delta >= threshold) {
        var normalizedDragDistance = (Math.abs(delta) / screenW);
        var opacity = 1 - Math.pow(normalizedDragDistance, 0.7);

        element.style.opacity = opacity;
    } else {
        e.currentTarget.childNodes[0].style.opacity = 1;
    }
}

function onRelease (e) {
    e.currentTarget.childNodes[0].style.opacity = 1;
}

#1


2  

The very nice people over at google chrome devs run a show call SuperCharged the idea behind the show is to show you quick and simple ways to make web apps effects.

在谷歌chrome开发公司工作的很不错的人会运行一个名为supercharge的节目,这个节目背后的想法是向你展示快速、简单的方法来让web应用程序产生效果。

They did one episode (which is about an hour long) about swipeable cards, they did a quick 10-minute episode just to give you the basic idea.

他们做了一集(大约一小时)关于可刷卡的节目,他们做了一个10分钟的节目只是为了给你一个基本的概念。

To answer your question javascript is the only way to make it respond, CSS doesn't respond to user input or action.

要回答您的问题,javascript是使它响应的唯一方式,CSS不响应用户输入或操作。

Also, it is best to use transform, as opposed to left, when moving things across the screen. They explain the reasons why in great detail during the show but the quick reason is transform can use the GPU.

此外,当在屏幕上移动物体时,最好使用transform,而不是向左。他们在节目中详细解释了原因,但快速的原因是转换可以使用GPU。

Anyway here is a live demo of the code they made in the episode, give it a glance and see if it's what you're looking for. I'd recommending watching their videos anyway, you can learn a lot (I certainly did).

不管怎样,这是他们在这集里所做的代码的一个现场演示,看看它是不是你要找的。无论如何,我建议你看看他们的视频,你可以学到很多东西(我当然学到了)。

/**
 Copyright 2016 Google Inc. All rights reserved.
 Licensed under the Apache License, Version 2.0 (the "License");
 */

'use strict';

class Cards {
  constructor() {
    this.cards = Array.from(document.querySelectorAll('.card'));

    this.onStart = this.onStart.bind(this);
    this.onMove = this.onMove.bind(this);
    this.onEnd = this.onEnd.bind(this);
    this.update = this.update.bind(this);
    this.targetBCR = null;
    this.target = null;
    this.startX = 0;
    this.currentX = 0;
    this.screenX = 0;
    this.targetX = 0;
    this.draggingCard = false;

    this.addEventListeners();

    requestAnimationFrame(this.update);
  }

  addEventListeners() {
    document.addEventListener('touchstart', this.onStart);
    document.addEventListener('touchmove', this.onMove);
    document.addEventListener('touchend', this.onEnd);

    document.addEventListener('mousedown', this.onStart);
    document.addEventListener('mousemove', this.onMove);
    document.addEventListener('mouseup', this.onEnd);
  }

  onStart(evt) {
    if (this.target)
      return;

    if (!evt.target.classList.contains('card'))
      return;

    this.target = evt.target;
    this.targetBCR = this.target.getBoundingClientRect();

    this.startX = evt.pageX || evt.touches[0].pageX;
    this.currentX = this.startX;

    this.draggingCard = true;
    this.target.style.willChange = 'transform';

    evt.preventDefault();
  }

  onMove(evt) {
    if (!this.target)
      return;

    this.currentX = evt.pageX || evt.touches[0].pageX;
  }

  onEnd(evt) {
    if (!this.target)
      return;

    this.targetX = 0;
    let screenX = this.currentX - this.startX;
    if (Math.abs(screenX) > this.targetBCR.width * 0.35) {
      this.targetX = (screenX > 0) ? this.targetBCR.width : -this.targetBCR.width;
    }

    this.draggingCard = false;
  }

  update() {

    requestAnimationFrame(this.update);

    if (!this.target)
      return;

    if (this.draggingCard) {
      this.screenX = this.currentX - this.startX;
    } else {
      this.screenX += (this.targetX - this.screenX) / 4;
    }

    const normalizedDragDistance =
      (Math.abs(this.screenX) / this.targetBCR.width);
    const opacity = 1 - Math.pow(normalizedDragDistance, 3);

    this.target.style.transform = `translateX(${this.screenX}px)`;
    this.target.style.opacity = opacity;

    // User has finished dragging.
    if (this.draggingCard)
      return;

    const isNearlyAtStart = (Math.abs(this.screenX) < 0.1);
    const isNearlyInvisible = (opacity < 0.01);

    // If the card is nearly gone.
    if (isNearlyInvisible) {

      // Bail if there's no target or it's not attached to a parent anymore.
      if (!this.target || !this.target.parentNode)
        return;

      this.target.parentNode.removeChild(this.target);

      const targetIndex = this.cards.indexOf(this.target);
      this.cards.splice(targetIndex, 1);

      // Slide all the other cards.
      this.animateOtherCardsIntoPosition(targetIndex);

    } else if (isNearlyAtStart) {
      this.resetTarget();
    }
  }

  animateOtherCardsIntoPosition(startIndex) {
    // If removed card was the last one, there is nothing to animate. Remove target.
    if (startIndex === this.cards.length) {
      this.resetTarget();
      return;
    }

    const frames = [{
      transform: `translateY(${this.targetBCR.height + 20}px)`
    }, {
      transform: 'none'
    }];
    const options = {
      easing: 'cubic-bezier(0,0,0.31,1)',
      duration: 150
    };
    const onAnimationComplete = () => this.resetTarget();

    for (let i = startIndex; i < this.cards.length; i++) {
      const card = this.cards[i];

      // Move the card down then slide it up.
      card
        .animate(frames, options)
        .addEventListener('finish', onAnimationComplete);
    }
  }

  resetTarget() {
    if (!this.target)
      return;

    this.target.style.willChange = 'initial';
    this.target.style.transform = 'none';
    this.target = null;
  }
}

window.addEventListener('load', () => new Cards());
/**
 Copyright 2016 Google Inc. All rights reserved. 
 Licensed under the Apache License, Version 2.0 (the "License");
 */

html,
body {
  margin: 0;
  padding: 0;
  background: #FAFAFA;
  font-family: Arial;
  font-size: 30px;
  color: #333;
}
* {
  box-sizing: border-box;
}
.card-container {
  width: 100%;
  max-width: 450px;
  padding: 16px;
  margin: 0 auto;
}
.card {
  background: #FFF;
  border-radius: 3px;
  box-shadow: 0 3px 4px rgba(0, 0, 0, 0.3);
  margin: 20px 0;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: space-around;
  cursor: pointer;
}
<!-- 
https://github.com/GoogleChrome/ui-element-samples/tree/master/swipeable-cards

https://www.youtube.com/watch?v=rBSY7BOYRo4

/**
 *
 * Copyright 2016 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-->

<div class="card-container">
  <div class="card">Das Surma</div>
  <div class="card">Aerotwist</div>
  <div class="card">Kinlanimus Maximus</div>
  <div class="card">Addyoooooooooo</div>
  <div class="card">Gaunty McGaunty Gaunt</div>
  <div class="card">Jack Archibungle</div>
  <div class="card">Sam "The Dutts" Dutton</div>
</div>

#2


0  

Ansrew's is very useful. In Ionic it is easier to use onDrag and onRelease directives.

Ansrew是非常有用的。在Ionic中,更容易使用onDrag和onRelease指令。

<ion-item on-drag="onDrag($event)" on-release="onRelease($event)" /> 

And use these methods to style the ion-item:

并使用这些方法对离子项进行样式化:

function onDrag (e) {
    var element = e.currentTarget.childNodes[0];
    var screenW = element.offsetWidth;
    var threshold = screenW * 0.16;

    var delta = Math.abs(e.gesture.deltaX);

    if(delta >= threshold) {
        var normalizedDragDistance = (Math.abs(delta) / screenW);
        var opacity = 1 - Math.pow(normalizedDragDistance, 0.7);

        element.style.opacity = opacity;
    } else {
        e.currentTarget.childNodes[0].style.opacity = 1;
    }
}

function onRelease (e) {
    e.currentTarget.childNodes[0].style.opacity = 1;
}