繁体   English   中英

如何在 angular 中实现翻转时钟

[英]How to implement flipclock in angular

我正在尝试使用 angular实现倒数计时器

问题是动画没有应用于值的变化,我错过了什么?

html

<div>
  <div class="flipclock" *ngIf="timer$ | async as timer">
    <div id="container" class="flipclock">
      <ul class="flip " *ngFor="let time of timer">
        <li
          *ngFor="let item of time.split(''); let i = index"
          [class.d1]="i === 1"
          [class.d2]="i === 0"
        >
          <section class="ready">
            <div class="up">
              <div class="shadow"></div>
              <div class="inn">{{ item }}</div>
            </div>
            <div class="down">
              <div class="shadow"></div>
              <div class="inn">{{ item }}</div>
            </div>
          </section>
          <section class="active">
            <div class="up">
              <div class="shadow"></div>
              <div class="inn">{{ item }}</div>
            </div>
            <div class="down">
              <div class="shadow"></div>
              <div class="inn">{{ item }}</div>
            </div>
          </section>
        </li>
      </ul>
    </div>
  </div>
</div>

export class AppComponent  {
  name = 'Angular ' + VERSION.major;
    initialMinutes$ = new BehaviorSubject(30);
  expired$ = new Subject();

  @Input()
  set minutes(val) {
    this.initialMinutes$.next(val);
  }

  timer$ = this.initialMinutes$.pipe(
    switchMap(minutes => timer(0, 1000).pipe(
      map(t => minutes * 60 - t),
      tap(seconds => {
        if (seconds < 0) {
          this.expired$.next();
        }
      }),
      takeUntil(this.expired$),
      map(seconds => ({
        hr: Math.max(Math.floor(seconds / 3600), 0),
        min: Math.max(Math.floor((seconds % 3600) / 60), 0),
        s: (seconds % 60)
      })),
      map(({hr, min, s}) => ([
        hr > 9 ? hr.toString() : '0' + hr.toString(),
        min > 9 ? min.toString() : '0' + min.toString(),
        s > 9 ? s.toString() : '0' + s.toString(),
      ]))
    ))
  );
}

css



.flipclock {
}
.flipclock hr {
  position: absolute;
  left: 0;
  top: 65px;
  width: 100%;
  height: 3px;
  border: 0;
  background: #000;
  z-index: 10;
  opacity: 0;
}
ul.flip {
  position: relative;
  float: left;
  margin: 10px;
  padding: 0;
  width: 90px;
  height: 60px;
  font-size: 60px;
  font-weight: 400;
  line-height: 60px;
}

ul.flip li {
  float: left;
  margin: 0;
  padding: 0;
  width: 49%;
  height: 100%;
  -webkit-perspective: 200px;
  list-style: none;
}

ul.flip li.d1 {
  float: right;
}

ul.flip li section {
  z-index: 1;
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;

}

ul.flip li section:first-child {
  z-index: 2;
}

ul.flip li div {
  z-index: 1;
  position: absolute;
  left: 0;
  width: 100%;
  height: 49%;
  overflow: hidden;
}

ul.flip li div .shadow {
  display: block;
  position: absolute;
  width: 100%;
  height: 100%;
  z-index: 2;
}

ul.flip li div.up {
  -webkit-transform-origin: 50% 100%;
  top: 0;
}

ul.flip li div.down {
  -webkit-transform-origin: 50% 0;
  bottom: 0;
}

ul.flip li div div.inn {
  position: absolute;
  left: 0;
  z-index: 1;
  width: 100%;
  height: 200%;
  color: #fff;
  text-shadow: 0 0 2px #fff;
  text-align: center;
  background-color: #000;
  border-radius: 6px;
}

ul.flip li div.up div.inn {
  top: 0;

}

ul.flip li div.down div.inn {
  bottom: 0;
}

/*--------------------------------------
 PLAY
--------------------------------------*/

.play ul section.ready {
  z-index: 3;
}

.play ul section.active {
  -webkit-animation: index .5s .5s linear both;
  z-index: 2;
}

@-webkit-keyframes index {
  0% {
    z-index: 2;
  }
  5% {
    z-index: 4;
  }
  100% {
    z-index: 4;
  }
}

.play ul section.active .down {
  z-index: 2;
  -webkit-animation: flipdown .5s .5s linear both;
}

@-webkit-keyframes flipdown {
  0% {
    -webkit-transform: rotateX(90deg);
  }
  80% {
    -webkit-transform: rotateX(5deg);
  }
  90% {
    -webkit-transform: rotateX(15deg);
  }
  100% {
    -webkit-transform: rotateX(0deg);
  }
}

.play ul section.ready .up {
  z-index: 2;
  -webkit-animation: flipup .5s linear both;
}

@-webkit-keyframes flipup {
  0% {
    -webkit-transform: rotateX(0deg);
  }
  90% {
    -webkit-transform: rotateX(0deg);
  }
  100% {
    -webkit-transform: rotateX(-90deg);
  }
}

/*--------------------------------------
 SHADOW
--------------------------------------*/

.play ul section.ready .up .shadow {
  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(0, 0, 0, .1)), color-stop(100%, rgba(0, 0, 0, 1)));
  background: linear-gradient(to bottom, rgba(0, 0, 0, .1) 0%, rgba(0, 0, 0, 1) 100%);
  -webkit-animation: show .5s linear both;
}

.play ul section.active .up .shadow {
  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(0, 0, 0, .1)), color-stop(100%, rgba(0, 0, 0, 1)));
  background: linear-gradient(to bottom, rgba(0, 0, 0, .1) 0%, rgba(0, 0, 0, 1) 100%);
  -webkit-animation: hide .5s .3s linear both;
}

/*DOWN*/

.play ul section.ready .down .shadow {
  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(0, 0, 0, 1)), color-stop(100%, rgba(0, 0, 0, .1)));
  background: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, .1) 100%);
  -webkit-animation: show .5s linear both;
}

.play ul section.active .down .shadow {
  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(0, 0, 0, 1)), color-stop(100%, rgba(0, 0, 0, .1)));
  background: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, .1) 100%);
  -webkit-animation: hide .5s .3s linear both;
}

@-webkit-keyframes show {
  0% {
    opacity: 0;
  }
  90% {
    opacity: .10;
  }
  100% {
    opacity: 1;
  }
}

@-webkit-keyframes hide {
  0% {
    opacity: 1;
  }
  80% {
    opacity: .20;
  }
  100% {
    opacity: 0;
  }
}

在 stackblitz 上查看此演示

编辑 1

我已经设法让动画反映,但它现在反映在所有项目上

在此处输入链接描述

我做了一个类似于“this”链接的计时器。(不要 go 的图像输出的 fps 更高)

在此处输入图像描述

这对你有用。

 var h = document.getElementsByClassName('hours'); var m = document.getElementsByClassName('min'); var s = document.getElementsByClassName('sec'); var now = new Date().getTime(); var distance = now; var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); var seconds = Math.floor((distance % (1000 * 60)) / 1000); var prev_sec = seconds; var prev_min = minutes; var prev_hour = hours; h[0].innerHTML = h[2].innerHTML = parseInt(hours / 10); h[1].innerHTML = h[3].innerHTML = parseInt(hours % 10); m[0].innerHTML = m[2].innerHTML = parseInt(minutes / 10); m[1].innerHTML = m[3].innerHTML = parseInt(minutes % 10); s[0].innerHTML = s[2].innerHTML = parseInt(seconds / 10); s[1].innerHTML = s[3].innerHTML = parseInt(seconds % 10); var x = setInterval(function() { // Get today's date and time var now = new Date().getTime(); // Find the distance between now and the count down date var distance = now; // Time calculations for days, hours, minutes and seconds var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); var seconds = Math.floor((distance % (1000 * 60)) / 1000); h[0].innerHTML = h[2].innerHTML = parseInt(hours / 10); h[1].innerHTML = h[3].innerHTML = parseInt(hours % 10); m[0].innerHTML = m[2].innerHTML = parseInt(minutes / 10); m[1].innerHTML = m[3].innerHTML = parseInt(minutes % 10); s[0].innerHTML = s[2].innerHTML = parseInt(seconds / 10); s[1].innerHTML = s[3].innerHTML = parseInt(seconds % 10); h[4].innerHTML = parseInt(prev_hour / 10); h[5].innerHTML = parseInt(prev_hour % 10); m[4].innerHTML = parseInt(prev_min / 10); m[5].innerHTML = parseInt(prev_min % 10); s[4].innerHTML = parseInt(prev_sec / 10); s[5].innerHTML = parseInt(prev_sec % 10); h[2].style.animation = parseInt(prev_hour / 10)?= parseInt(hours / 10): "reflect 1s infinite"; "stay 1s infinite". h[3].style?animation = parseInt(prev_hour % 10):= parseInt(hours % 10); "reflect 1s infinite". "stay 1s infinite". m[2]?style:animation = parseInt(prev_min / 10);= parseInt(minutes / 10). "reflect 1s infinite". "stay 1s infinite"? m[3]:style;animation = parseInt(prev_min % 10).= parseInt(minutes % 10). "reflect 1s infinite"? "stay 1s infinite": s[2];style.animation = parseInt(prev_sec / 10).= parseInt(seconds / 10); "reflect 1s infinite"; "stay 1s infinite"; s[3],style;animation = "reflect 1s infinite"; prev_sec = seconds; prev_min = minutes prev_hour = hours; }, 1000);
 * { font-family: 'arial'; font-weight: bold; }.main_Timer { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }.time { clip-path: polygon(0 0, 100% 0, 100% 50%, 0 50%); }.time span, .time_reflection span, .time_prev span { color: white; font-size: 120px; background-color: #252525; border-radius: 10px; padding: 5px 10px 0px 10px; transition: 0.4s; text-align: center; margin-right: 3px; } @keyframes time { 0% { filter: brightness(70%); } 90%, 100% { filter: brightness(100%); } }.line_hor { position: absolute; background-color: white; width: 650px; height: 2px; margin-top: -72px; z-index: 10; }.perspective { position: absolute; perspective: 1400px; perspective-origin: 10% 0; z-index: 5; margin-top: -143px; }.perspective.time_reflection { position: absolute; transform-style: preserve-3d; display: flex; }.time_reflection span { display: inline-block; clip-path: polygon(0 50%, 100% 50%, 100% 100%, 0 100%); }.time_prev { display: flex; margin-top: -143px; position: absolute; clip-path: polygon(0 50%, 100% 50%, 100% 100%, 0 100%); z-index: 4; filter: brightness(50%); }.time_prev span {} @keyframes stay { from { transform: rotateX(0deg); } to { transform: rotateX(0deg); } } @keyframes reflect { 0% { transform: rotateX(130deg); opacity: 1; } 45% { transform: rotateX(0deg); } 50% { transform: rotateX(7deg); } 53% { transform: rotateX(0deg); } 56% { transform: rotateX(5deg); } 60% { transform: rotateX(0deg); } 95% { transform: rotateX(0deg); } 100% { transform: rotateX(0deg); } }.hours:nth-child(2n), .min:nth-child(2n), .sec:nth-child(2n) { margin-right: 25px; }
 <div class="main_Timer"> <div class="clock"> <div class="time"> <span class="hours">0</span><span class="hours">0</span><span class="min">0</span><span class="min">0</span><span class="sec">0</span><span class="sec">0</span> </div> </div> <div class="perspective"> <div class="time_reflection"> <span class="hours">0</span><span class="hours">0</span><span class="min">0</span><span class="min">0</span><span class="sec">0</span><span class="sec">0</span> </div> </div> <div class="time_prev"> <span class="hours">0</span><span class="hours">0</span><span class="min">0</span><span class="min">0</span><span class="sec">0</span><span class="sec">0</span> </div> <div class="line_hor"></div> </div>

我们可以使用 angular 动画来做到这一点。 一种方法(它与您的 css 不同)是从边框底部制作“垂直翻转”。 在一个面上你有一个数字,而在另一个面上另一个数字。 好吧,我们真的需要 ot 有完整的数字,否则是数字的一半。

想象一下你有一些喜欢

<div class="content">
    <div class="flip">
        <div class="up">
            <div>{{oldvalue}}</div>
        </div>
        <div class="down">
            <div>
                <div>{{oldvalue}}</div>
            </div>
        </div>
    </div>
</div>

.css 就像

.content {
  font-family: "Droid Sans Mono", monospace;
  height: 60px;
  display:inline-block;
  margin-left:10px;
}
.flip {
  position: relative;
  height: 60px;
  width: 45px;
}
.up,
.down {
  text-align: center;
  height: 30px;
  overflow: hidden;
}
.up > div,
.down > div {
  font-size: 50px;
  font-weight: 800;
  line-height: 60px;
  align-self: center;
}
.down > div > div {
  margin-top: -30px;
}

有了这个,你有一个两半的数字

然后我们可以使用经典的垂直翻转

<div class="content">
    <div class="flip-card">
        <div class="flip-card-inner" [@flip]="value">
            <div class="flip-card-front">
                <div class="up">
                    <div>{{oldvalue}}</div>
                </div>
            </div>
            <div class="flip-card-back">
                <div class="down">
                    <div>
                        <div>{{newvalue}}</div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

.css

.flip-card {
  perspective: 300px;
  position: relative;
  height: 30px;
  width: 45px;
  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
}

.flip-card-inner {
  width: 100%;
  height: 100%;
  text-align: center;
  transform-style: preserve-3d;
  -ms-transform-origin: 50% 100%; /* IE 9 */
  transform-origin: 50% 100%; /* IE 9 */
}

.flip-card-front,
.flip-card-back {
  position: absolute;
  width: 100%;
  height: 100%;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  overflow: hidden;
}
.flip-card-back {
  transform: rotateX(180deg);
}

animation 真的很简单

  animations:[
    trigger("flip",[
      transition('*=>*',[
      animate(".6s",keyframes([
        style({transform:"rotateX(0deg)",offset: 0}),
        style({transform:"rotateX(-90deg)",offset: .5}),
        style({transform:"rotateX(-180deg)",offset: 1}),
      ]))
    ])
  ])]

好吧,完整的数字现在是 div 之前的两个

<div class="content">
    <div style="position:absolute">
        <--here the digit-->
    </div>
    <div style="position:absolute">
        <---here the flip-card--->
    </div>
</div>

你可以看到第一个 stackblitz ,看看当你点击按钮时,它是如何为数字设置动画的

那么,如何使用这一切。 正如 Ac_mmi 所说,您需要具有旧值和新值。 You can use your code adding a map to return an array of six numbers, a pipe pairwise to get the old and the value and a map to return an array of object with the two properties:

map(val => val.map(i => i.split("")).reduce((a, b) => [...a, ...b], [])),
pairwise(),
map(([old,value])=>{
  return value.map((x,index)=>({value:x,old:old[index]}))
})

然后我们在 [0,1,2,3,4,5] 上做一个简单的循环来获取数字

<ng-container *ngIf="timer$ |async as timer">
  <div class="content" *ngFor="let i of [0,1,2,3,4,5]">
  ....
</ng-container>

最终的堆栈闪电战

更新

使用@Ac_mmi 提供的 class 和 animation,您定义了一个 animation

  animations:[
    trigger("flip",[
      transition('*=>*',[
      animate(".6s",keyframes([
        style({transform:"rotateX(130deg)",offset: 0}),
        style({transform:"rotateX(0deg)",offset: .45}),
        style({transform:"rotateX(7deg)",offset: .50}),
        style({transform:"rotateX(0deg)",offset: .53}),
        style({transform:"rotateX(5deg)",offset: .56}),
        style({transform:"rotateX(0deg)",offset: .60}),
        style({transform:"rotateX(0deg)",offset: .95}),
        style({transform:"rotateX(0deg)",offset: 1}),
      ]))
    ])
  ])]

并且动画元素变得像,例如“小时”

<span class="hours" [@flip]="timer[0].value">
   {{timer[0].value}}
 </span>

你有另一个堆栈闪电战

Update2获取小时、分钟和秒,我们可以使用 formatDate -function 使用 Angular for DatePipe- 并且计时器变得更加简单:

  timer$ = this.initialMinutes$.pipe(
    map(minutes => minutes * 60000 + new Date().getTime()),
    switchMap(minutes =>
      timer(0, 500).pipe(
        map(t => formatDate((minutes - new Date().getTime()),"HHmmss","en-US","+0000").split('')),
        takeUntil(this.expired$),
        pairwise(),
        map(([old,value])=>{
          return value.map((x,index)=>({value:x,old:old[index]}))
        })
      )
    )
  );

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM