Browse Source

Volume controls for video

master
morethanwords 4 years ago
parent
commit
ae8f0755b7
  1. 10
      src/lib/logger.ts
  2. 126
      src/lib/mediaPlayer.ts
  3. 4
      src/scss/partials/_chatBubble.scss
  4. 186
      src/scss/partials/_ckin.scss
  5. 2
      src/scss/partials/_rightSidebar.scss

10
src/lib/logger.ts

@ -16,7 +16,7 @@ export function logger(prefix: string, level = LogLevels.log | LogLevels.warn |
level = LogLevels.error; level = LogLevels.error;
} }
level = LogLevels.log | LogLevels.warn | LogLevels.error | LogLevels.debug //level = LogLevels.log | LogLevels.warn | LogLevels.error | LogLevels.debug
function Log(...args: any[]) { function Log(...args: any[]) {
return level & LogLevels.log && console.log(dT(), '[' + prefix + ']:', ...args); return level & LogLevels.log && console.log(dT(), '[' + prefix + ']:', ...args);
@ -38,13 +38,13 @@ export function logger(prefix: string, level = LogLevels.log | LogLevels.warn |
return level & LogLevels.log && console.trace(dT(), '[' + prefix + ']:', ...args); return level & LogLevels.log && console.trace(dT(), '[' + prefix + ']:', ...args);
}; };
Log.debug = function(...args: any[]) { /* Log.debug = function(...args: any[]) {
return level & LogLevels.debug && console.log(dT(), '[' + prefix + ']:', ...args); return level & LogLevels.debug && console.log(dT(), '[' + prefix + ']:', ...args);
}; }; */
/* Log.debug = function(...args: any[]) { Log.debug = function(...args: any[]) {
return level & LogLevels.debug && console.debug(dT(), '[' + prefix + ']:', ...args); return level & LogLevels.debug && console.debug(dT(), '[' + prefix + ']:', ...args);
}; */ };
return Log; return Log;
}; };

126
src/lib/mediaPlayer.ts

@ -1,9 +1,11 @@
import { cancelEvent } from "./utils";
export class ProgressLine { export class ProgressLine {
public container: HTMLDivElement; public container: HTMLDivElement;
protected filled: HTMLDivElement; protected filled: HTMLDivElement;
protected seek: HTMLInputElement; protected seek: HTMLInputElement;
protected duration = 100; protected duration = 1;
protected mousedown = false; protected mousedown = false;
private events: Partial<{ private events: Partial<{
@ -13,22 +15,26 @@ export class ProgressLine {
onScrub: (scrubTime: number) => void onScrub: (scrubTime: number) => void
}> = {}; }> = {};
constructor() { constructor(initialValue = 0) {
this.container = document.createElement('div'); this.container = document.createElement('div');
this.container.classList.add('media-progress'); this.container.classList.add('progress-line');
this.filled = document.createElement('div'); this.filled = document.createElement('div');
this.filled.classList.add('media-progress__filled'); this.filled.classList.add('progress-line__filled');
const seek = this.seek = document.createElement('input'); const seek = this.seek = document.createElement('input');
seek.classList.add('media-progress__seek'); seek.classList.add('progress-line__seek');
seek.value = '0'; seek.value = '' + initialValue;
seek.setAttribute('min', '0'); seek.setAttribute('min', '0');
seek.setAttribute('max', '0'); //seek.setAttribute('max', '0');
seek.type = 'range'; seek.type = 'range';
seek.step = '0.1'; seek.step = '0.1';
seek.max = '' + (this.duration * 1000); seek.max = '' + (this.duration * 1000);
if(initialValue > 0) {
this.setProgress(initialValue);
}
//this.setListeners(); //this.setListeners();
this.container.append(this.filled, seek); this.container.append(this.filled, seek);
@ -45,28 +51,37 @@ export class ProgressLine {
onMouseDown = (e: MouseEvent) => { onMouseDown = (e: MouseEvent) => {
this.scrub(e); this.scrub(e);
this.mousedown = true; this.mousedown = true;
this.events?.onMouseDown(e); this.events?.onMouseDown && this.events.onMouseDown(e);
}; };
onMouseUp = (e: MouseEvent) => { onMouseUp = (e: MouseEvent) => {
this.mousedown = false; this.mousedown = false;
this.events?.onMouseUp(e); this.events?.onMouseUp && this.events.onMouseUp(e);
}; };
protected setListeners() { public setListeners() {
this.container.addEventListener('mousemove', this.onMouseMove); this.container.addEventListener('mousemove', this.onMouseMove);
this.container.addEventListener('mousedown', this.onMouseDown); this.container.addEventListener('mousedown', this.onMouseDown);
this.container.addEventListener('mouseup', this.onMouseUp); this.container.addEventListener('mouseup', this.onMouseUp);
} }
protected scrub(e: MouseEvent) { public setProgress(scrubTime: number) {
const scrubTime = e.offsetX / this.container.offsetWidth * this.duration; this.setFilled(scrubTime);
this.seek.value = '' + (scrubTime * 1000);
}
public setFilled(scrubTime: number) {
let scaleX = scrubTime / this.duration; let scaleX = scrubTime / this.duration;
scaleX = Math.max(0, Math.min(1, scaleX)); scaleX = Math.max(0, Math.min(1, scaleX));
this.filled.style.transform = 'scaleX(' + scaleX + ')'; this.filled.style.transform = 'scaleX(' + scaleX + ')';
}
//this.events?.onScrub(scrubTime); protected scrub(e: MouseEvent) {
const scrubTime = e.offsetX / this.container.offsetWidth * this.duration;
this.setFilled(scrubTime);
this.events?.onScrub && this.events.onScrub(scrubTime);
return scrubTime; return scrubTime;
} }
@ -90,7 +105,7 @@ export class MediaProgressLine extends ProgressLine {
if(streamable) { if(streamable) {
this.filledLoad = document.createElement('div'); this.filledLoad = document.createElement('div');
this.filledLoad.classList.add('media-progress__filled', 'media-progress__loaded'); this.filledLoad.classList.add('progress-line__filled', 'progress-line__loaded');
this.container.prepend(this.filledLoad); this.container.prepend(this.filledLoad);
//this.setLoadProgress(); //this.setLoadProgress();
} }
@ -197,15 +212,13 @@ export class MediaProgressLine extends ProgressLine {
} }
} }
protected setProgress() { public setProgress() {
const currentTime = this.media.currentTime; const currentTime = this.media.currentTime;
const scaleX = (currentTime / this.duration); super.setProgress(currentTime);
this.filled.style.transform = 'scaleX(' + scaleX + ')';
this.seek.value = '' + currentTime * 1000;
} }
protected setListeners() { public setListeners() {
super.setListeners(); super.setListeners();
this.media.addEventListener('ended', this.onEnded); this.media.addEventListener('ended', this.onEnded);
this.media.addEventListener('play', this.onPlay); this.media.addEventListener('play', this.onPlay);
@ -230,6 +243,7 @@ export class MediaProgressLine extends ProgressLine {
} }
} }
let lastVolume = 1, muted = !lastVolume;
export default class VideoPlayer { export default class VideoPlayer {
public wrapper: HTMLDivElement; public wrapper: HTMLDivElement;
public progress: MediaProgressLine; public progress: MediaProgressLine;
@ -275,6 +289,65 @@ export default class VideoPlayer {
var timeDuration = player.querySelector('#time-duration') as HTMLElement; var timeDuration = player.querySelector('#time-duration') as HTMLElement;
timeDuration.innerHTML = String(video.duration | 0).toHHMMSS(); timeDuration.innerHTML = String(video.duration | 0).toHHMMSS();
const volumeDiv = document.createElement('div');
volumeDiv.classList.add('player-volume');
volumeDiv.innerHTML = `
<svg class="player-volume__icon" focusable="false" viewBox="0 0 24 24" aria-hidden="true"></svg>
`;
const volumeSvg = volumeDiv.firstElementChild as SVGSVGElement;
volumeSvg.addEventListener('click', (e) => {
cancelEvent(e);
if(!lastVolume) {
muted = true;
lastVolume = 1;
}
const realVolume = !muted ? 0 : lastVolume;
setVolume(realVolume);
volumeProgress.setProgress(realVolume);
muted = !muted;
});
const setVolume = (volume: number) => {
let d: string;
if(volume > .5) {
d = `M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z`;
} else if(!volume) {
d = `M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z`;
} else if(volume > 0 && volume < .25) {
d = `M7 9v6h4l5 5V4l-5 5H7z`;
} else {
d = `M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z`;
}
try {
volumeSvg.innerHTML = `<path d="${d}"></path>`;
} catch(err) {}
video.volume = volume;
};
const fakeVolume = muted ? 0 : lastVolume;
video.volume = fakeVolume;
setVolume(fakeVolume);
const volumeProgress = new ProgressLine(muted ? 0 : fakeVolume);
volumeProgress.setListeners();
volumeProgress.setHandlers({
onScrub: currentTime => {
const value = Math.max(Math.min(currentTime, 1), 0);
console.log('scrub', currentTime, value);
setVolume(lastVolume = value);
}
});
volumeDiv.append(volumeProgress.container);
const leftControls = player.querySelector('.left-controls');
leftControls.insertBefore(volumeDiv, timeElapsed.parentElement);
Array.from(toggle).forEach((button) => { Array.from(toggle).forEach((button) => {
return button.addEventListener('click', () => { return button.addEventListener('click', () => {
this.togglePlay(); this.togglePlay();
@ -402,9 +475,8 @@ export default class VideoPlayer {
private buildControls() { private buildControls() {
const skin = this.skin; const skin = this.skin;
const html: string[] = [];
if(skin === 'default') { if(skin === 'default') {
html.push(` return `
<button class="${skin}__button--big toggle tgico-largeplay" title="Toggle Play"></button> <button class="${skin}__button--big toggle tgico-largeplay" title="Toggle Play"></button>
<div class="${skin}__gradient-bottom ckin__controls"></div> <div class="${skin}__gradient-bottom ckin__controls"></div>
<div class="${skin}__controls ckin__controls"> <div class="${skin}__controls ckin__controls">
@ -421,14 +493,14 @@ export default class VideoPlayer {
<button class="${skin}__button fullscreen tgico-fullscreen" title="Full Screen"></button> <button class="${skin}__button fullscreen tgico-fullscreen" title="Full Screen"></button>
</div> </div>
</div> </div>
</div>`); </div>`;
} else if(skin === 'circle') { } else if(skin === 'circle') {
html.push('<svg class="progress-ring" width="200px" height="200px">', return `
'<circle class="progress-ring__circle" stroke="white" stroke-opacity="0.3" stroke-width="3.5" cx="100" cy="100" r="93" fill="transparent" transform="rotate(-90, 100, 100)"/>', <svg class="progress-ring" width="200px" height="200px">
'</svg>'); <circle class="progress-ring__circle" stroke="white" stroke-opacity="0.3" stroke-width="3.5" cx="100" cy="100" r="93" fill="transparent" transform="rotate(-90, 100, 100)"/>
</svg>
`;
} }
return html.join('');
} }
public updateButton(toggle: NodeListOf<HTMLElement>) { public updateButton(toggle: NodeListOf<HTMLElement>) {

4
src/scss/partials/_chatBubble.scss

@ -754,7 +754,7 @@ $bubble-margin: .25rem;
} }
} }
.media-progress { .progress-line {
/* width: calc(100% + 50px); */ /* width: calc(100% + 50px); */
width: 191px; width: 191px;
margin: 9px 0 9px; margin: 9px 0 9px;
@ -1202,7 +1202,7 @@ $bubble-margin: .25rem;
} }
.message.audio-message { .message.audio-message {
.media-progress { .progress-line {
&__seek { &__seek {
background: rgba(193, 207, 220, 0.39); background: rgba(193, 207, 220, 0.39);
} }

186
src/scss/partials/_ckin.scss

@ -1,6 +1,27 @@
.ckin { .ckin {
&__player { &__player {
letter-spacing: 0.02em; letter-spacing: 0.02em;
&.ckin__fullscreen {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
height: 100%;
width: 100%;
z-index: 10000000;
background: #000;
border-radius: 0 !important;
display: -ms-flexbox;
display: flex;
video {
max-height: none;
max-width: none;
object-fit: contain;
}
}
} }
&__overlay { &__overlay {
@ -16,27 +37,6 @@
} }
} }
.ckin__player.ckin__fullscreen {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
height: 100%;
width: 100%;
z-index: 10000000;
background: #000;
border-radius: 0 !important;
display: -ms-flexbox;
display: flex;
video {
max-height: none;
max-width: none;
object-fit: contain;
}
}
.default { .default {
border: 0 solid rgba(0, 0, 0, 0.2); border: 0 solid rgba(0, 0, 0, 0.2);
box-shadow: 0 0 20px rgba(0, 0, 0, 0.2); box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
@ -120,73 +120,105 @@
direction: ltr; direction: ltr;
border-radius: 0 0 5px 5px; border-radius: 0 0 5px 5px;
z-index: 6; z-index: 6;
.progress-line {
margin: 0 16px;
height: 5px;
background: rgba(255, 255, 255, 0.38);
border-radius: 4px;
overflow: visible;
&__filled {
background: #63a2e3;
transform-origin: left;
border-radius: 4px;
height: 5px;
transform: scaleX(0);
}
&__loaded {
background: rgba(255, 255, 255, 0.38);
left: 11px;
width: calc(100% - 11px);
}
}
} }
}
.default__gradient-bottom { &__gradient-bottom {
height: 49px; height: 49px;
// padding-top: 49px; // padding-top: 49px;
padding-top: 93px; padding-top: 93px;
bottom: 0; bottom: 0;
z-index: 2; z-index: 2;
background-position: bottom; background-position: bottom;
width: 100%; width: 100%;
position: absolute; position: absolute;
background-repeat: repeat-x; background-repeat: repeat-x;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==); background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==);
transition: all .3s; transition: all .3s;
pointer-events: none; pointer-events: none;
} }
.default.is-playing .default__gradient-bottom { &.is-playing {
transform: translateY(50px); .default__gradient-bottom {
} transform: translateY(50px);
}
html.no-touch .default.is-playing:hover .default__gradient-bottom { html.no-touch &:hover {
transform: translateY(0px); .default__gradient-bottom {
} transform: translateY(0px);
}
.default.is-playing:before { .default__controls {
opacity: 0; transform: translateY(0);
visibility: hidden; }
transform: translate(-50%, -50%) scale(1.3); }
}
.default.is-playing .default__button--big { &:before {
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
} transform: translate(-50%, -50%) scale(1.3);
}
.default.is-playing .default__controls { .default__button--big {
transform: translateY(52px); opacity: 0;
} visibility: hidden;
}
html.no-touch .default.is-playing:hover .default__controls { .default__controls {
transform: translateY(0); transform: translateY(52px);
} }
}
.default { .player-volume {
.media-progress { margin: -3px 12px 0 16px;
margin: 0 16px; display: flex;
height: 5px; align-items: center;
transition: height 0.3s;
background: rgba(255, 255, 255, 0.38); &__icon {
border-radius: 4px; fill: #fff;
overflow: visible; width: 24px;
height: 24px;
&__filled { margin-right: 8px;
background: #63a2e3; cursor: pointer;
transform-origin: left;
border-radius: 4px;
height: 5px;
transform: scaleX(0);
} }
&__loaded { .progress-line {
background: rgba(255, 255, 255, 0.38); margin: 0;
left: 11px; width: 50px;
width: calc(100% - 11px);
&__filled {
background: #fff;
}
input[type=range]::-webkit-slider-thumb {
height: 15px;
width: 15px;
border-radius: 16px;
background: #fff;
}
} }
} }
} }
@ -200,7 +232,7 @@ video::-webkit-media-controls-enclosure {
display: none !important; display: none !important;
} }
.media-progress { .progress-line {
position: relative; position: relative;
cursor: pointer; cursor: pointer;

2
src/scss/partials/_rightSidebar.scss

@ -466,7 +466,7 @@
} }
} }
.media-progress { .progress-line {
margin: 11px 0 8px; margin: 11px 0 8px;
&__filled { &__filled {

Loading…
Cancel
Save