SpotDJ背后的故事

SpotDJ是一个无服务器的Web应用程序,任何拥有Spotify帐户的人都可以通过共享唯一的URL与他们的朋友或同事一起播放DJ。 任何访问该URL的侦听器都将使他们的Spotify播放器通过SpotDJ自动与DJ同步-很好玩!

介绍

作为背景,我是一名初级开发人员,并且在8个月前开始全职编程。 SpotDJ完全是用ReasonML编写的,对于我来说,实现非常有趣,因为这只是我大约四个月前的第一个专业Reason项目之后的第二个ReasonML Web应用程序。 SpotDJ是与Xavier Cazalot( @ xavxyz ),他对探索ReasonML并在Internet上创建产品感到非常兴奋,而肖恩·格罗夫( @ sgrove)则对在OneGraph上构建应用程序感到兴奋。

我对该项目的目标是深入研究使用ReasonML的Web应用程序前端实现,尤其是动画和CSS-in-js。

为此,SpotDJ主要是用

  • OneGraph用于处理API和身份验证
  • 用于实现WebRTC的PeerJS
  • UI动画的反应运动与情感

我将描述我在设计SpotDJ时的高级想法以及我在下面尝试过的一些低级实验,但首先,请在https://spotdj.onegraphapp.com/上尝试一下!

第1阶段-基本的Spotify播放器活动和渲染

首先,我想从Spotify的一些真实数据开始构建基本结构。 有了骨头,以后我可以更轻松地添加设计的内容。

认证!

首先,我需要代表用户处理访问Spotify API的授权(在这个阶段,只有我一个人)。 到目前为止,这是该项目中最不有趣的部分,通常非常令人沮丧。 但是,OneGraph通过其身份验证库使此操作非常容易。 我花了大约10分钟的时间来登录/注销逻辑,另外花了大约30分钟的时间来实现页面并连接操作。

获取数据!

总共,我在基本基础架构上花费了不到一个小时的时间,包括Spotify授权,通过Spotify API获取实时数据,以及编写BuckleScript绑定以将OneGraph身份验证库与Reason一起使用。 OneGraph使与Spotify API的交互变得如此轻松和简单,以至于在整个开发过程中,我从未访问过Spotify的API文档页面。

通过构建基本的页面结构和功能,我可以深入研究项目中更有趣的一面-完善页面的UI / UX。

动画:从“登录提示”到“ DJ /观众视图”的过渡

用户登录到SpotDJ时看到的第一件事是登录页面,要求用户向我们提供通过其帐户访问Spotify API的权限。 但是,登录后,页面将切换到DJ / Audience视图。 如果没有平稳的过渡,则两页之间的跳转会导致空白屏幕短暂闪烁,并会造成一时的混乱(“屏幕空白,应用程序还可以吗?”)。

为了提供更连续的体验,我们需要向用户发送信号, “您仍在SpotDJ应用程序中”。 Xavier提出了一种非常简单有效的方式来表达此信息,它可以防止页面标题的重新呈现。 页面标题的持续存在可防止黑屏闪烁,因此在页面转换时可提供流畅的体验。

动画:中间加载屏幕

用户登录后,我们立即开始从Spotify API提取数据。 但是,尽管他们的API可以达到最快的速度,但是仍然有一个(希望)短暂的加载期,我们才能在页面上显示播放器信息。

我们可以简单地渲染一些文字,表示正在loading...或在页面上显示微调框。 但是,由于加载时间往往非常短,因此用户甚至可能没有足够的时间来完成单词“ loading”的阅读。 因此,我们应用了淡入动画,以使加载阶段不明显。

尽管加载时间会因多种原因而有所不同,但我们仍应处理加载时间超过淡入动画持续时间的情况。 对于SpotDJ的初始版本,我选择跳过这种情况。 但是考虑到SpotDJ v2,我想显示一个带有灰色形状的基本占位符,以传达加载状态。 而且,如果加载时间超过三秒钟,我们会显示一条更直接的消息或进度栏。 不过,这些将是不错的功能。 但是与此同时,我们的淡入动画可以满足基本的UX需求。

动画:声波,播放器状态与暂停

我们需要与用户交流的最重要的信息之一就是玩家状态-Spotify当前正在播放还是暂停?

显示传统的播放/暂停图标可能会造成混乱,因为我们不允许用户通过SpotDJ控制自己的Spotify播放器。 看到播放/暂停图标可能会使用户误以为他们可以单击该图标并播放或暂停播放器,而不是认为它是只读状态指示器。

我想要一种更好的方式来传达玩家的状态,并提出了声波动画的想法。

最初,我想通过更改可见声音块的数量来模仿波动。 但是,总共有24个块(3列,每个8块)。 为每个块自定义动画时间是一项繁重的工作。

当我使用“每块动画”方法实现声波时,感觉像是一件乏味的工作。 我开始怀疑是否还有另一种更好的方法(或者说是懒惰的)。

“如果必要是发明之母,那么懒惰就是父亲。”

我意识到,改变每个声波列的高度,并使用简单的overflow:hide属性也可以实现移动的声波效果。 这是一个明显更简单(更容易)的实现! 但是,此实现的一个副作用是,随着音柱高度的变化,声音块会平滑地从中间切开。 尽管如此,虽然我不确定其他UI设计师会如何考虑我的快捷方式,但在这种情况下,我是设计师,所以我会做主! 我们不必总是在设计中保持历史准确性,因此我看不出不应将声音块切穿中间的任何原因。

我的决定也与更简单,更有效的声波动画实现相吻合,这只是一个巧合。 大概。 也许。

第2阶段-通过“无服务器” p2p WebRTC Spotify同步策略支持“服务器”机构

即使OneGraph处理了我们所有对所需数据的身份验证和API访问,我们仍然需要将DJ的播放器状态复制到侦听器中。 我的第一个想法是使用服务器,但这会带来很多复杂性和单点故障,这不好。 再一次,l̶a̶z̶i̶n̶e̶s的侧面思考来进行救援!

如果我们使用WebRTC完全取消服务器怎么办? 然后用户甚至可以git自己克隆应用程序并在自己的本地主机上运行两个实例时进行同步! 而且我不必编写或运行服务器! 双赢。 赢得。

我决定使用PeerJS(似乎是一个全面的WebRTC库)来发送同步数据。 在DJ端,我们使用OneGraph在Spotify上查找当前播放的歌曲及其位置。 然后,我们将该数据广播给DJ的所有听众,他们每隔大约1秒钟左右检查一次他们的Spotify播放器状态。 最后,我们比较两个播放器的状态-如果两个播放器上的trackId不匹配,或者progressMs(歌曲位置)不同步超过10s,我们在监听器侧使用OneGraph同步trackId和progressMs。一个电话。 很酷

DJ:大家好,我在听Avicii的3:20-Muja

听众1:恩,我在听《刀》的1:20-心跳! 我会切换到你的歌😀

听众1:嘿,OneGraph! 您介意演奏Avicii的3:20-Muja吗?

听众2:我正在听《阿维奇》的3:15-Muja,距离足够近

听众3:什么?!? 我在Avicii的3:01回来了-Muja! OneGraph,让我着迷!

OneGraph:侦听器1,我支持您-完成!

OneGraph:侦听器3,您现在回到3:20!

现在,我们有一个功能齐全的SpotDJ,可以:

1.显示播放器信息

2.发送/接收Spotify播放器数据

3.同步播放器状态

太酷了!

不过,我还是想让SpotDJ更具互动性。 如果听众喜欢一首歌,并且想了解更多有关该歌曲的信息,但是在他们回到应用程序之前,这首歌就结束了– SpotDJ跳到下一首歌,永远注定我们的听众想知道那首歌是什么。

这将是一个黑暗的世界,无畏的用户无法向后追踪! 我们决不能让这样的世界出来!

让我们显示播放歌曲的简单历史记录。

显示歌曲历史记录的最节省空间的方法是使用简单列表。 但这有点平淡,当然不是很有趣。 我想使SpotDJ成为一个有趣且引人入胜的网络应用程序,使人们在玩游戏时会发现一些快乐。

让我们使用著名的覆盖流动画之类的东西来显示轨道历史,而不是历史列表。

封面流程尝试1:通过CSS的播放器历史记录(CSS-in-JS)

在我第一次尝试实现封面流程时,我使用CSS关键帧来动画动画从左向左,并决定最多显示三个历史轨迹(这是因为我们不想要一个完全黑暗的世界,但也想要那个Pobody的nerfect)。

新歌开始时,每个音轨都向左移动一个位置,因此当前播放音轨始终保持居中。 我使用了弹性流来定位轨道组件。 由于我使用的是React(或者说是ReasonReact),我通过将轨道的数组索引分配为元素的键来触发动画,从而迫使轨道组件重新渲染。

但是,请看下面的结果:动画不是很流畅,协调所有片段的时间安排非常具有挑战性,而且感觉并不十分互动。

覆盖流尝试2:ReactMotion

因此,如果我们想要一个流畅的交互式历史记录列表,css-animations-in-js可能不是走的路。 另外,仅显示前三个轨道感觉太妥协了。 我们必须争取一个更加光明,更有能力的世界。

相反,我决定显示整个历史记录(在性能太差之前,用户必须听很多不同的曲目),并允许用户流畅地水平滚动显示较旧的曲目。 为此,在看了成城在React中关于动画的ReactEurope 2015演讲后,我决定尝试一下ReactMotion。 我认为这可能给我带来两个好处:

1.通过弹簧使动画运动看起来更逼真

2.通过将所有内容移动到具有动态值的样式中来简化时间安排和定位

我绝对不依赖浏览器正确地列出曲目列表,而是绝对自己定位元素并根据组件中的内部状态翻译它们。 采用这种方法,我必须做一些额外的工作来使当前播放的曲目在第一次加载时居中(根据页面宽度等来计算居中位置)和调整大小。

然后,我手动计算每个附加项目的水平位置,将较旧的项目放在左侧(因为当我考虑时间时,较旧的项目向左侧退出,而较新的项目则从右侧进入)。

我最初使用此方法遇到的一个错误是我只能在项目向右溢出时滚动,而在项目向左溢出时滚动。 我不确定为什么-这是问题的代码示例,如果您知道的话,请告诉我,这样我可以向您发送一些非常真诚的互联网积分,也谢谢我。

我尝试的解决方法是添加另一个在右侧溢出的不可见子元素列表,并在首次加载组件时触发父容器滚动到最右边元素的末尾。 这样,默认情况下,当前正在播放的曲目位于屏幕的中央,我可以滚动查看左侧溢出的项目。 但是,此方法仅适用于足够长的元素列表。 overflow:scroll需要元素列表足够长以触发溢出属性(毫不奇怪)。

在这个令人烦恼的溢出滚动问题上苦苦挣扎超过半天之后,我意识到我可以只使用鼠标滚轮事件了—完全没有溢出,非常感谢!

我们根据事件的deltaX计算差异,然后通过translation css属性将更改应用到track组件。 这是一项需要更多计算的工作,但它可以解决“向左滚动”的问题,并最终为元素提供更大的灵活性和控制力。

未来:SpotDJ 2.0的里程碑

对于SpotDJ 2.0,我想:

  • 添加动画以处理加载时间较长的情况
  • 使用WebRTC的媒体功能向DJ的听众添加语音广播-为其带来真实的现场广播感觉
  • 代替简单的以DJ为中心的系统,实现一个互连的系统,在该系统中,侦听器可以兼用作DJ,从而形成连接到嵌套DJ的侦听器链。 一种人的区块链。

尝试一下! 真! 现在就试试!

同样,这是SpotDJ的链接:https://spotdj.onegraphapp.com/

尝试一下。 与您的朋友分享。 推特。

并请分享您对设计和实现的任何想法和建议! 查阅源代码,并在GitHub存储库上查找相关问题!