Skip to content

Instantly share code, notes, and snippets.

@stanwmusic
Last active October 12, 2017 12:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stanwmusic/214e6cc42e9e12769a59cbe7ee7e5a7f to your computer and use it in GitHub Desktop.
Save stanwmusic/214e6cc42e9e12769a59cbe7ee7e5a7f to your computer and use it in GitHub Desktop.
Last Trail Audio Player
<div class="player paused">
<div class="album">
<div class="cover">
<div><img src="https://www.gimptalk.org/wp-content/uploads/2017/10/PeacePipe_new1440Sqr-URLs-300x300.jpg" alt="Last Trail by Stan Williams" /></div>
</div>
</div>
<div class="info">
<div class="time">
<span class="current-time">0:00</span>
<span class="progress"><span></span></span>
<span class="duration">0:00</span>
</div>
<h1>Last Trail</h1>
<h2>Stan Williams</h2>
</div>
<div class="actions">
<button class="shuffle">
<div class="arrow"></div>
<div class="arrow"></div>
</button>
<button class="button rw">
<div class="arrow"></div>
<div class="arrow"></div>
</button>
<button class="button play-pause">
<div class="arrow"></div>
</button>
<button class="button ff">
<div class="arrow"></div>
<div class="arrow"></div>
</button>
<button class="repeat"></button>
</div>
<audio prelaod src="https://ia600403.us.archive.org/5/items/StanWilliamsLastTrail/StanWilliamsLastTrail.mp3"></audio>
</div>

Last Trail Audio Player

A skeuomorphic audio player made to look like spinning vinyl record. Design made using pure CSS and a single image for the album artwork. Controls done using minimal jQuery.

A Pen by Stan Williams on CodePen.

License.

Peace Pipe Audio Player

A skeuomorphic audio player made to look like spinning vinyl record. Design made using pure CSS and a single image for the album artwork. Controls done using minimal jQuery.

A Pen by Stan Williams on CodePen.

License.

var player = $('.player'),
audio = player.find('audio'),
duration = $('.duration'),
currentTime = $('.current-time'),
progressBar = $('.progress span'),
mouseDown = false,
rewind, showCurrentTime;
function secsToMins(time) {
var int = Math.floor(time),
mins = Math.floor(int / 60),
secs = int % 60,
newTime = mins + ':' + ('0' + secs).slice(-2);
return newTime;
}
function getCurrentTime() {
var currentTimeFormatted = secsToMins(audio[0].currentTime),
currentTimePercentage = audio[0].currentTime / audio[0].duration * 100;
currentTime.text(currentTimeFormatted);
progressBar.css('width', currentTimePercentage + '%');
if (player.hasClass('playing')) {
showCurrentTime = requestAnimationFrame(getCurrentTime);
} else {
cancelAnimationFrame(showCurrentTime);
}
}
audio.on('loadedmetadata', function() {
var durationFormatted = secsToMins(audio[0].duration);
duration.text(durationFormatted);
}).on('ended', function() {
if ($('.repeat').hasClass('active')) {
audio[0].currentTime = 0;
audio[0].play();
} else {
player.removeClass('playing').addClass('paused');
audio[0].currentTime = 0;
}
});
$('button').on('click', function() {
var self = $(this);
if (self.hasClass('play-pause') && player.hasClass('paused')) {
player.removeClass('paused').addClass('playing');
audio[0].play();
getCurrentTime();
} else if (self.hasClass('play-pause') && player.hasClass('playing')) {
player.removeClass('playing').addClass('paused');
audio[0].pause();
}
if (self.hasClass('shuffle') || self.hasClass('repeat')) {
self.toggleClass('active');
}
}).on('mousedown', function() {
var self = $(this);
if (self.hasClass('ff')) {
player.addClass('ffing');
audio[0].playbackRate = 2;
}
if (self.hasClass('rw')) {
player.addClass('rwing');
rewind = setInterval(function() { audio[0].currentTime -= .3; }, 100);
}
}).on('mouseup', function() {
var self = $(this);
if (self.hasClass('ff')) {
player.removeClass('ffing');
audio[0].playbackRate = 1;
}
if (self.hasClass('rw')) {
player.removeClass('rwing');
clearInterval(rewind);
}
});
player.on('mousedown mouseup', function() {
mouseDown = !mouseDown;
});
progressBar.parent().on('click mousemove', function(e) {
var self = $(this),
totalWidth = self.width(),
offsetX = e.offsetX,
offsetPercentage = offsetX / totalWidth;
if (mouseDown || e.type === 'click') {
audio[0].currentTime = audio[0].duration * offsetPercentage;
if (player.hasClass('paused')) {
progressBar.css('width', offsetPercentage * 100 + '%');
}
}
});
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
html { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; }
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
color: #666;
font-size: 100%;
font-weight: 300;
font-family: 'Roboto Condensed', sans-serif;
background-color: #222;
}
h1 {
margin-bottom: 0.5em;
font-weight: 400;
}
h2 { font-size: 87.5%; }
.player {
position: relative;
width: 20em;
min-height: 20em;
overflow: hidden;
background-color: #eee;
border-radius: 0.25em;
box-shadow:
0 1.5em 2em -1em rgba(0,0,0,0.8),
inset 0 0.0625em 0 rgba(255,255,255,1),
inset 0 -0.125em 0.0625em rgba(0,0,0,0.3);
}
.album {
position: relative;
left: 50%;
width: 15em;
height: 15em;
margin-bottom: -13%;
overflow: hidden;
transform: translate(-50%,-25%);
background-color: #111;
border: 1px solid #111;
border-radius: 50%;
box-shadow:
0 0.0625em 0.1875em rgba(0,0,0,0.5),
0 0 0.125em 0.3125em #ddd,
0 0.0625em 0 0.375em #bbb,
0 0 0.375em 0.325em rgba(0,0,0,0.3),
0 0 0.5em 0.375em rgba(0,0,0,0.3),
0 0.25em 1em 0.5em rgba(0,0,0,0.15),
inset 0 0 0 0.0625em rgba(0,0,0,0.5),
inset 0 0 0 0.1875em rgba(255,255,255,1),
inset 0 0 0 0.375em rgba(0,0,0,0.5),
inset 0 0 0 0.4375em rgba(255,255,255,0.2),
inset 0 0 0 0.5em rgba(0,0,0,0.5),
inset 0 0 0 0.5625em rgba(255,255,255,0.3),
inset 0 0 0 0.625em rgba(0,0,0,0.5),
inset 0 0 0 0.6875em rgba(255,255,255,0.2),
inset 0 0 0 0.75em rgba(0,0,0,0.5),
inset 0 0 0 0.8125em rgba(255,255,255,0.3),
inset 0 0 0 0.875em rgba(0,0,0,0.5),
inset 0 0 0 0.9375em rgba(255,255,255,0.3),
inset 0 0 0 1em rgba(0,0,0,0.5),
inset 0 0 0 1.0625em rgba(255,255,255,0.2),
inset 0 0 0 1.125em rgba(0,0,0,0.5),
inset 0 0 0 1.1875em rgba(255,255,255,0.3),
inset 0 0 0 1.25em rgba(0,0,0,0.5),
inset 0 0 0 1.3125em rgba(255,255,255,0.2),
inset 0 0 0 1.375em rgba(255,255,255,0.2),
inset 0 0 0 1.4375em rgba(0,0,0,0.5),
inset 0 0 0 1.5em rgba(255,255,255,0.3),
inset 0 0 0 1.5625em rgba(0,0,0,0.5),
inset 0 0 0 1.625em rgba(255,255,255,0.3),
inset 0 0 0 1.6875em rgba(0,0,0,0.5),
inset 0 0 0 1.75em rgba(255,255,255,0.2),
inset 0 0 0 1.8125em rgba(0,0,0,0.5),
inset 0 0 0 1.875em rgba(255,255,255,0.2),
inset 0 0 0 1.9375em rgba(0,0,0,0.5),
inset 0 0 0 2em rgba(255,255,255,0.3),
inset 0 0 0 2.0625em rgba(0,0,0,0.5),
inset 0 0 0 2.125em rgba(0,0,0,0.5),
inset 0 0 0 2.1875em rgba(255,255,255,0.1),
inset 0 0 0 2.25em rgba(0,0,0,0.5),
inset 0 0 0 2.3125em rgba(255,255,255,0.2),
inset 0 0 0 2.375em rgba(255,255,255,0.1),
inset 0 0 0 2.4375em rgba(0,0,0,0.5),
inset 0 0 0 2.5em rgba(255,255,255,0.3),
inset 0 0 0 2.5625em rgba(0,0,0,0.5),
inset 0 0 0 2.625em rgba(255,255,255,0.2),
inset 0 0 0 2.6875em rgba(0,0,0,0.5),
inset 0 0 0 2.75em rgba(255,255,255,0.2),
inset 0 0 0 2.8125em rgba(0,0,0,0.5),
inset 0 0 0 2.875em rgba(255,255,255,0.2),
inset 0 0 0 2.9375em rgba(0,0,0,0.5),
inset 0 0 0 3em rgba(255,255,255,0.3),
inset 0 0 0 3.0625em rgba(0,0,0,0.5),
inset 0 0 0 3.125em rgba(0,0,0,0.5),
inset 0 0 0 3.1875em rgba(255,255,255,0.2),
inset 0 0 0 3.25em rgba(0,0,0,0.5),
inset 0 0 0 3.3125em rgba(255,255,255,0.2),
inset 0 0 0 3.375em rgba(255,255,255,0.1),
inset 0 0 0 3.4375em rgba(0,0,0,0.5),
inset 0 0 0 3.5em rgba(255,255,255,0.3);
}
.album::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
transform: translate(-50%,-50%);
background-image:
linear-gradient(
-45deg,
rgba(255,255,255,0) 30%,
rgba(255,255,255,0.125),
rgba(255,255,255,0) 70%
),
linear-gradient(
-48deg,
rgba(255,255,255,0) 45%,
rgba(255,255,255,0.075),
rgba(255,255,255,0) 55%
),
linear-gradient(
-42deg,
rgba(255,255,255,0) 45%,
rgba(255,255,255,0.075),
rgba(255,255,255,0) 55%
),
radial-gradient(
circle at top left,
rgba(0,0,0,1) 20%,
rgba(0,0,0,0) 80%
),
radial-gradient(
circle at bottom right,
rgba(0,0,0,1) 20%,
rgba(0,0,0,0) 80%
);
}
.cover,
.cover div {
position: absolute;
z-index: 1;
top: 50%;
left: 50%;
width: 6em;
height: 6em;
overflow: hidden;
transform-origin: 0 0;
transform: rotate(0) translate(-50%,-50%);
border-radius: 50%;
animation: spin 4s linear infinite paused;
}
.ffing .cover {
animation-play-state: running;
}
.cover div {
border-radius: 0;
animation: spin 2s linear infinite reverse paused;
}
.rwing .cover div {
animation: spin 2s linear infinite reverse running;
}
.cover::before,
.cover::after {
content: '';
position: absolute;
z-index: 10;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
transform-origin: 0 0;
transform: rotate(0) translate(-50%,-50%);
border-radius: 50%;
box-shadow: inset 0 0.0625em rgba(255,255,255,0.3);
animation: spin 4s linear infinite reverse paused;
}
.cover::after {
width: 0.25em;
height: 0.3125em;
margin-top: -0.0625em;
background-color: #eee;
border-radius: 0.125em;
box-shadow:
inset 0 -0.0625em 0.0625em rgba(0,0,0,0.5),
inset 0.0625em -0.0625em 0.125em rgba(255,255,255,0.15),
inset -0.0625em -0.0625em 0.125em rgba(255,255,255,0.15),
inset 0 -0.125em 0.125em rgba(0,0,0,0.8),
0 0.0625em 0.0625em rgba(0,0,0,0.5),
0 0.0625em 0.25em 0.0625em rgba(0,0,0,0.15),
0 0 0.25em 0.125em rgba(0,0,0,0.15);
}
.ffing .cover::before,
.ffing .cover::after {
animation-play-state: running;
}
.cover img {
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
transform-origin: 0 0;
transform: rotate(0) translate(-50%,-50%);
animation: spin 4s linear infinite paused;
}
.paused .cover img {
animation-play-state: paused;
}
.playing .cover img {
animation-play-state: running;
}
.info {
text-align: center;
text-shadow: 0 0.0625em rgba(255,255,255,1);
}
.time {
display: flex;
justify-content: center;
align-items: center;
padding: 0 0.5em;
margin-bottom: 0.5em;
}
.time > * {
margin: 0 0.5em;
}
.progress {
flex-grow: 2;
height: 0.125em;
background-color: #999;
border-radius: 0.0625em;
box-shadow: 0 0.0625em rgba(255,255,255,1);
cursor: pointer;
}
.progress span {
display: block;
width: 0;
height: 100%;
background-color: #666;
}
.actions {
position: relative;
width: 100%;
padding: 1em 0 1.125em;
display: flex;
justify-content: center;
align-items: center;
}
button {
appearance: none;
outline: none;
position: relative;
padding: 0;
font-size: 100%;
background-color: transparent;
border: none;
cursor: pointer;
}
.button {
width: 3em;
height: 3em;
background-color: transparent;
background-image: linear-gradient(#ddd, #f6f6f6);
border: none;
border-radius: 50%;
}
.button::before {
content: '';
position: absolute;
z-index: 1;
top: 50%;
left: 50%;
width: 80%;
height: 80%;
transform: translate(-50%,-50%);
background-color: #f4f4f4;
border: 0.125em solid #d5d5d5;
border-radius: 50%;
box-shadow: inset 0 0.25em 1em -0.25em rgba(255,255,255,0.75);
}
.button:hover::before {
background-color: #fcfcfc;
}
.play-pause {
width: 4em;
height: 4em;
}
.rw {
right: -0.25em;
margin-left: 0.375em;
transform: scaleX(-1);
}
.ff {
left: -0.25em;
margin-right: 0.375em;
}
.button .arrow {
position: absolute;
z-index: 10;
top: 50%;
left: 50%;
width: 30%;
height: 30%;
overflow: hidden;
transform: translate(-50%,-50%);
}
.button .arrow::before,
.button .arrow::after {
content: '';
position: absolute;
left: -50%;
width: 100%;
height: 100%;
transform: scale(1.2,0.7) rotate(45deg);
background-color: turquoise; /* was #ddd; */
box-shadow:
inset 0 0.125em 0.125em -0.0625em rgba(0,0,0,0.15),
0.0625em 0.0625em 0.125em rgba(255,255,255,1);
}
.button .arrow::after {
left: 0;
transform: none;
background-color: transparent;
box-shadow: inset 0.0625em 0 0.125em -0.0625em rgba(0,0,0,0.1);
}
.paused .play-pause .arrow {
margin-left: 0.1875em;
}
.playing .play-pause .arrow::before,
.playing .play-pause .arrow::after {
left: 0;
width: 0.4375em;
transform: none;
background-color: #333; /* was ddd */
box-shadow:
inset 0.0625em 0.125em 0.125em -0.0625em rgba(0,0,0,0.15),
0.0625em 0.0625em 0.125em rgba(255,255,255,1);
}
.playing .play-pause .arrow::after {
left: auto;
right: 0;
}
.rw .arrow,
.ff .arrow {
width: 20%;
height: 20%;
margin-left: 12%;
}
.rw .arrow:first-child,
.ff .arrow:first-child {
margin-left: -4%;
}
.button:active .arrow::before,
.playing .play-pause .arrow::before,
.playing .play-pause .arrow::after {
background-color: #33c;
}
.shuffle {
width: 1.375em;
height: 1.375em;
color: #d5d5d5;
}
.shuffle .arrow {
position: absolute;
top: 0.1875em;
left: 0;
width: 0.375em;
height: 0.125em;
color: inherit;
background-color: currentColor;
}
.shuffle .arrow::before {
content: '';
position: absolute;
top: 0;
left: calc(100% + 0.125em);
width: 0.5em;
height: 1em;
transform: skewX(30deg);
border-bottom: 0.125em solid;
border-left: 0.125em solid;
box-shadow:
-0.3125em 0em 0 -0.1875em #eee,
inset 0.375em 0.25em 0 -0.25em #eee;
}
.shuffle .arrow::after {
content: '';
position: absolute;
top: 0.6875em;
left: calc(100% + 0.625em);
border: 0.25em solid transparent;
border-left-width: 0.375em;
border-left-color: currentColor;
}
.shuffle .arrow:first-child {
transform-origin: 0 0.5em;
transform: scaleY(-1);
}
.repeat {
width: 1.375em;
height: 1.375em;
color: #d5d5d5;
border: 0.125em solid;
border-right-color: transparent;
border-radius: 50%;
}
.repeat::before {
content: '';
position: absolute;
top: -0.125em;
left: -0.125em;
width: calc(100% + 0.25em);
height: calc(100% + 0.25em);
transform: rotate(-45deg);
border: 0.125em solid transparent;
border-right-color: currentColor;
border-radius: 50%;
}
.repeat::after {
content: '';
position: absolute;
top: 50%;
right: -0.3125em;
border: 0.25em solid transparent;
border-top-width: 0.375em;
border-top-color: currentColor;
}
.shuffle.active,
.repeat.active {
color: #33f;
}
@keyframes spin {
100% { transform: rotate(360deg) translate(-50%,-50%); }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment