javascript 如何制作 HTML5 旋转列表/旋转轮选择器/选择器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33157307/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 16:11:11  来源:igfitidea点击:

How to make a HTML5 spinning list/rotating wheel selector/picker

javascriptcsshtmlanimation

提问by Emily

My question is about an answer given by user ByteHamster here: How to create JavaScript/HTML5 Spinner List with images?in the answer he/she gives an example of how to create a scrolling animation with html, css and Javascript. The animation allows the user to scroll through the numbers by clicking above or below the selected number on screen, and the selected number is shown in a div below the animation.

我的问题是关于用户 ByteHamster 在这里给出的答案:How to create JavaScript/HTML5 Spinner List with images? 在回答中,他/她举例说明了如何使用 html、css 和 Javascript 创建滚动动画。动画允许用户通过单击屏幕上所选数字的上方或下方来滚动数字,所选数字显示在动画下方的 div 中。

I was wondering if it's possible to do something similar to this but instead of having an image moving up and down, can it be turned into a number wheel? By that I mean, in the example above, the scrolling stops in one direction once the number reaches 0, I'm wondering if it's possible to create a wheel, where a user could constantly spin it from top to bottom, or bottom to top if they wished to do so. Would this require using 3d interactive animation software?

我想知道是否可以做类似的事情,但不是让图像上下移动,它可以变成数字轮吗?我的意思是,在上面的示例中,一旦数字达到 0,滚动就会停止在一个方向上,我想知道是否可以创建一个轮子,用户可以在其中不断地从上到下或从下到上旋转它如果他们愿意的话。这是否需要使用 3d 交互式动画软件?

I've seen this question: HTML5/CSS3 - how to make "endless" rotating background - 360 degrees panoramabut I'm unsure if the answers are applicable to my project as they don't seem to be interactive.

我见过这个问题:HTML5/CSS3 - 如何制作“无限”旋转背景 - 360 度全景,但我不确定这些答案是否适用于我的项目,因为它们似乎不是交互式的。

As user ByteHamster's answer is over 3 years old, I was wondering if there's a better way to achieve this effect with a html5 animation? And am I correct in thinking that the Javascript in the example would make it not work on some devices/browsers that don't have Javascript enabled? Would a html5 approach be the best way to ensure the effect works on most devices/browsers?

由于用户 ByteHamster 的回答已经超过 3 年了,我想知道是否有更好的方法来使用 html5 动画实现这种效果?我是否认为示例中的 Javascript 会使其无法在某些未启用 Javascript 的设备/浏览器上运行?html5 方法是确保效果适用于大多数设备/浏览器的最佳方法吗?

回答by Shikkediel

Here's what I put together from the info provided... works with the mousewheel, swiping and clicking on the top and bottom numbers. Infinite as requested of course. No special perspective style (yet) but I thought it looked quite decent as is. Could still be an option naturally. Didn't use the plugin I linked to in the comments or requestAnimationFramebut jQuery animate()is quite a good tool for this. The library has great cross browser support (that's it's strength actually), all it needs is a link to it for the JavaScript to be able to get executed. You could use a CDN, this version also works for IE8- :

这是我根据所提供的信息汇总的内容...使用鼠标滚轮,滑动并单击顶部和底部数字。当然是无限的。没有特殊的透视风格(还),但我认为它看起来很不错。自然仍然可以选择。没有使用我在评论中链接的插件,或者requestAnimationFramejQueryanimate()是一个很好的工具。该库具有出色的跨浏览器支持(实际上就是它的优势),它所需要的只是一个链接,以便 JavaScript 能够执行。您可以使用 CDN,此版本也适用于 IE8-:

<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>

For the best cross browser support on using the mousewheel, this plugin was included :

为了在使用鼠标滚轮时获得最佳的跨浏览器支持,包含了这个插件:

<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.js"></script>

https://plugins.jquery.com/mousewheel/

https://plugins.jquery.com/mousewheel/

Just a basic parent and styling with spans for each number, a few prepended in case of going up.

只是一个基本的父级和每个数字的跨度样式,在上升的情况下会预先设置一些。

$(function() {

var gate = $(window),
cog = $('#rotator'),
digit = cog.find('span'),
field = $('#result'),
slot = digit.height(),
base = 1.5*slot,
up, swipe;

if (document.readyState == 'complete') interAction();
else gate.one('load', interAction);

function interAction() {

 field.text(0);

 cog.scrollTop(base).fadeTo(0,1).mousewheel(function(turn, delta) {

  if (isBusy()) return false;

  up = delta > 0;
  nextNumber();

  return false;
 });

 digit.on('touchstart', function(e) {

  var begin = e.originalEvent.touches[0].pageY;

  digit.on('touchmove', function(e) {

   var yaw = e.originalEvent.touches[0].pageY-begin;
   up = yaw < 0;
   swipe = Math.abs(yaw) > 30;
  });

  gate.one('touchend', function() {

   digit.off('touchmove');

   if (swipe && !isBusy()) nextNumber();
  });
 })
 .on('mousedown touchstart', function(e) {

  if (e.which && e.which != 1) return;

  var zest = this, item = $(this).index();

  $(this).one('mouseup touchend', function(e) {

   digit.off('mouseup');

   var quit = e.originalEvent.changedTouches;

   if (quit) var jab = document.elementFromPoint(quit[0].clientX, quit[0].clientY);
   if (swipe || item == 2 || quit && jab != zest || isBusy()) return;

   up = item == 1;
   nextNumber();
  });

  return false;
 })
 .mouseleave(function() {

  digit.off('mouseup');
 });
}

function isBusy() {

 return cog.is(':animated');
}

function nextNumber() {

 var aim = base;
 swipe = false;

 up ? aim += slot : aim -= slot;

 cog.animate({scrollTop: aim}, 250, function() {

  up ? digit.eq(0).appendTo(cog) : digit.eq(9).prependTo(cog);

  digit = cog.find('span');

  cog.scrollTop(base);
  field.text(digit.eq(2).text());
 });
}
});
body {
  background: grey;
}

#ticker {
  width: 150px;
  text-align: center;
  margin: auto;
}

#rotator {
  height: 140px;
  font-family: "Times New Roman";
  font-size: 50px;
  line-height: 70px;
  background-image:
  url(http://ataredo.com/external/image/flip.png),
  url(http://ataredo.com/external/image/flip.png),
  url(http://ataredo.com/external/image/flip.png);
  background-position: 0 0, 50% 50%, 100% 150%;
  background-size: 300% 50%;
  background-repeat: no-repeat;
  margin: 0 0 10px;
  overflow: hidden;
  opacity: 0;
}

#rotator span {
  width: 100%;
  height: 50%;
  display: inline-block;
  cursor: default;
  -webkit-tap-highlight-color: rgba(0,0,0,0);
  -webkit-tap-highlight-color: transparent;
}

#result {
  height: 30px;
  font-size: 20px;
  color: white;
  line-height: 30px;
  letter-spacing: 3px;
  -webkit-box-shadow: 0 0 3px black;
  box-shadow: 0 0 3px black;
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.js"></script>

<div id="ticker">
 <div id="rotator">
   <span>8</span>
   <span>9</span>
   <span>0</span>
   <span>1</span>
   <span>2</span>
   <span>3</span>
   <span>4</span>
   <span>5</span>
   <span>6</span>
   <span>7</span>
 </div>
 <div id="result"></div>
</div>

Pretty straightforward, animates the scroll position up or down and then appends or prepends the first or last number depending on the direction. The duration of the animation can be set here :

非常简单,向上或向下滚动位置动画,然后根据方向附加或添加第一个或最后一个数字。动画的持续时间可以在这里设置:

cog.animate({scrollTop: current}, 250, function() {


Updated - after having some new insights, for example the touchendevent always firing on the original element, I've overhauled the code. Besides that, it now has a sprite background that will stay in proportion with the size of the numbers themselves. Also improved the overall logic and removed a nested listener glitch.

更新 - 在有了一些新的见解之后,例如touchend事件总是在原始元素上触发,我对代码进行了大修。除此之外,它现在有一个精灵背景,将与数字本身的大小成比例。还改进了整体逻辑并删除了嵌套的侦听器故障。

Another reason for this edit is to insert a demo that allows to have multiple tickers (and preset the numbers). As I've even moved on beyond that (adding direct response functionality), I thought it would be a good idea to leave the minimal working code for that here as well:

进行此编辑的另一个原因是插入一个允许有多个股票代码(并预设数字)的演示。由于我什至已经超越了这一点(添加了直接响应功能),我认为在这里也留下最少的工作代码是个好主意:

$(function() {

var gate = $(window),
orb = document,
cog = $('.rotator'),
field = $('#result'),
slot = cog.height()/2,
base = 1.5*slot,
list = [],
niche = [7,7,7],
term = 250, // duration of animation
mass, up = true,
yaw = 'mousemove.ambit touchmove.ambit',
hike = 'mouseup.turf touchend.turf',
part = 'mouseleave.range';

tallyCells();

if (orb.readyState == 'complete') interAction();
else gate.one('load', interAction);

gate.on('mouseleave touchcancel', function(e) {

 !(e.type == 'mouseleave' && e.relatedTarget) && lotRelease();
});

function interAction() {

cog.scrollTop(base).each(function(unit) {

 var pinion = $(this),
 digit = pinion.find('.quota'),
 cipher = Number(niche[unit])%10 || 0;
 list[unit] = digit;
 niche[unit] = 0;
 field.append(0);

 for (var i = 0; i < cipher; i++) nextNumber(pinion, unit, true);

 pinion.mousewheel(function(turn, delta) {

  if (isBusy(pinion)) return false;

  up = delta > 0;
  nextNumber(pinion, unit);

  return false;
 });

 digit.on('mousedown touchstart', function(e) {

  if (e.which && e.which != 1) return;

  var zest = this, ken = {}, item = $(this).index();

  tagPoints(e, ken);

  digit.on(part, wipeSlate).on(hike, function(e) {

   wipeSlate();

   var quit = e.originalEvent.changedTouches;

   if (quit) var jab = orb.elementFromPoint(quit[0].clientX, quit[0].clientY);
   if (item == 2 || quit && jab != zest || isBusy(pinion)) return;

   up = item == 1;
   nextNumber(pinion, unit);
  });

  gate.on(yaw, function(e) {

   hubTrace(e, ken);
  })
  .on(hike, function() {

   lotRelease();

   if (!ken.hit || isBusy(pinion)) return;

   up = ken.way < 0;
   nextNumber(pinion, unit);
  });

  return false;
 });

}).fadeTo(0,1);

function tagPoints(act, bit) {

 var nod = act.originalEvent.touches;
 bit.mark = nod ? nod[0].pageY : act.pageY;
 bit.veer = false;
}

function hubTrace(task, gob) {

 var peg = task.originalEvent.touches,
 fly = peg ? peg[0].pageY : task.pageY;
 gob.way = fly-gob.mark;
 gob.hit = Math.abs(gob.way) > 30;

 if (!gob.veer && gob.hit) {
 gob.veer = true;
 wipeSlate();
 }
}

function wipeSlate() {

 mass.off(part + ' ' + hike);
}

function isBusy(whirl) {

 return whirl.is(':animated');
}

function nextNumber(aim, knob, quick) {

 var intent = base, hook = list[knob];

 up ? intent += slot : intent -= slot;

 if (quick) {
 aim.scrollTop(intent);
 revolveTooth();
 }
 else aim.animate({scrollTop: intent}, term, revolveTooth);

function revolveTooth() {

 up ? hook.eq(0).appendTo(aim) : hook.eq(9).prependTo(aim);

 list[knob] = aim.find('.quota');
 niche[knob] = Number(list[knob].eq(2).text());

 aim.scrollTop(base);
 field.text(niche.join(''));
}
}
}

function lotRelease() {

 gate.off(yaw).add(mass).off(hike);
 mass.off(part);
}

function tallyCells() {

 cog.each(function() {

  for (var i = 0; i < 10; i++) {

  var n; !i ? n = 8 : (i == 1 ? n = 9 : n = i-2);

  $(this).append('<div></div>').find('div').eq(i).text(n).addClass('quota');
  }
 });

 mass = $('.quota');
}
});
body {
  text-align: center;
  background: grey;
}

#ticker, .rotator {
  display: inline-block;
}

.rotator {
  width: 100px;
  height: 140px;
  font-family: "Times New Roman";
  font-size: 50px;
  line-height: 80px;
  background-image:
  url(http://ataredo.com/external/image/flip.png),
  url(http://ataredo.com/external/image/flip.png),
  url(http://ataredo.com/external/image/flip.png);
  background-position: 0 0, 50% 50%, 100% 150%;
  background-size: 300% 50%;
  background-repeat: no-repeat;
  margin: 0 0 10px;
  overflow: hidden;
  opacity: 0;
}

.quota {
  height: 50%;
  cursor: default;
  -webkit-tap-highlight-color: rgba(0,0,0,0);
  -webkit-tap-highlight-color: transparent;
}

#result {
  height: 35px;
  font-size: 20px;
  color: white;
  line-height: 35px;
  letter-spacing: 3px;
  -webkit-box-shadow: 0 0 3px black;
  box-shadow: 0 0 3px black;
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.js"></script>

<div id="ticker">
  <div class="rotator"></div>
  <div class="rotator"></div>
  <div class="rotator"></div>
  <div id="result"></div>
</div>

It will populate the numbers automatically so there's no need to write the markup. Responds to mouse dragging as well.

它将自动填充数字,因此无需编写标记。也响应鼠标拖动。



The latest evolution of the script can be found here :

可以在此处找到脚本的最新演变:

codepen.io/Shikkediel/pen/avVJdG

codepen.io/Shikkediel/pen/avVJdG



Final update - a 3d version that uses transitioninstead of jQuery .animate. The wheel is made up of individually rotated elements around the x-axis, creating a basically infinite decagon without the need for prepending or appending elements:

最终更新 - 使用transitionjQuery 代替的3d 版本.animate。轮子由绕 x 轴单独旋转的元素组成,创建一个基本上无限的十边形,而无需预先或附加元素:

codepen.io/Shikkediel/pen/qpjGyq

codepen.io/Shikkediel/pen/qpjGyq

The cogs are "flickable", making them progress at the speed that the user gives them - then stop again when clicking. They also respond much quicker to mouse wheel events than the original demo. Both reasons why I've left out click events, as opposed to the earlier scripts. Browser support is also a bit more limited but good nonetheless - I've made an extra effort to make it IE10+ compatible.

齿轮是“可轻弹的”,使它们以用户给它们的速度前进 - 然后在单击时再次停止。它们对鼠标滚轮事件的响应也比原始演示快得多。与早期的脚本相反,我忽略了点击事件的两个原因。浏览器支持也有一些限制,但仍然很好 - 我已经做出了额外的努力使其与 IE10+ 兼容。