CSS/JS: Change opacity on swipe

ぃ、小莉子 提交于 2020-08-25 04:05:19

问题


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

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

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

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>

回答1:


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.

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.

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

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.

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:


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

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


来源:https://stackoverflow.com/questions/38350116/css-js-change-opacity-on-swipe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!