plyr icon indicating copy to clipboard operation
plyr copied to clipboard

double tap to fast forward

Open AnasProgrammer2 opened this issue 4 years ago • 12 comments

hello , please update player with feature ( double tap to fast forward +5 Sec or more )

AnasProgrammer2 avatar Mar 29 '21 11:03 AnasProgrammer2

well coincidently i was going to write a issue like this but found this one. so i am in favor for this feature

amk200 avatar Mar 29 '21 11:03 amk200

well coincidently i was going to write a issue like this but found this one. so i am in favor for this feature

thanks ,

AnasProgrammer2 avatar Mar 29 '21 12:03 AnasProgrammer2

well coincidently i was going to write a issue like this but found this one. so i am in favor for this feature

waiting your update

AnasProgrammer2 avatar Mar 29 '21 12:03 AnasProgrammer2

Can add just buttons to forward 10sec?

AnasProgrammer2 avatar Mar 29 '21 15:03 AnasProgrammer2

Can add just buttons to forward 10sec?

that might also be great, let's wait for a dev/maintainer/owner of this project to view it. All the best to awesome devs who built this player 😄

amk200 avatar Mar 29 '21 17:03 amk200

I personally like this feature as well. Attached are how Facebook and Twitter handle this on mobile devices.

  • Facebook has -10s and +10s icon buttons
  • Twitter's fast forward & rewind is handled by a "double click" towards the edges of the video, i.e. "right edge" -> fast forward, "left edge" -> rewind. These actions stack, so if you click often enough onto the right edge, the UI will display that you fast-forwarded 20s, for example.

Interesting to point out, and to keep in mind, this behavior is only implemented for touch-screen/mobile devices. It does not exist in the desktop experience on either platform.

Here are 3 screenshots of how Twitter and Facebook handle it:

chrisbbreuer avatar Apr 09 '21 21:04 chrisbbreuer

i suggest if not double click, then the facebook type buttons could be added after checking if the device is a mobile, tablet device or a laptop, PC

amk200 avatar Apr 10 '21 06:04 amk200

Any news about this feature?

denis-mironov avatar Sep 01 '21 10:09 denis-mironov

No news as of now, although I have managed to inject my custom buttons using some code after the player loads. Screenshot_20210901_190802 I use brython in my projects so you can translate this into javascript if needed. Icons are svg images from google's icon set. Here is the code used:

<script type="text/javascript">
            function insertAfter(newNode, existingNode) {
                existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling);
            }
        </script>
<script type="text/python">
            from browser import document, window, html, bind, timer


            def wait_and_init():
                elm = document.select(".plyr__controls__item")[0]
                forward_elm = html.IMG("", **{"src": "{{ url_for('static', filename='static/images/forward_10_white.svg') }}",
                                              "class": "qcontrols",
                                              "id": "forward-10"})
                replay_elm = html.IMG("", **{"src": "{{ url_for('static', filename='static/images/replay_10_white.svg') }}",
                                              "class": "qcontrols",
                                              "id": "replay-10"})
                window.insertAfter(forward_elm, elm)
                window.insertAfter(replay_elm, elm)

                @bind(replay_elm, "click")
                def replay_10_seconds(evt):
                    window.player.rewind(10)

                @bind(forward_elm, "click")
                def forward_10_seconds(evt):
                    window.player.forward(10)

            timer.set_timeout(wait_and_init, 1000)
                
        </script>

amk200 avatar Sep 01 '21 13:09 amk200

Interestingly, I have managed to build the double click feature from this page but with some modifications to make it look just a little better. Here is the scripting part and html part (all in javascript):

First add the following css to your website:

<style type="text/css">
        .player {
  width:100%;
  border: 5px solid rgba(0,0,0,0.2);
  box-shadow: 0 0 20px rgba(0,0,0,0.2);
  position: relative;
  font-size: 0;
  overflow: hidden;
}

video{
  width:100%;
  display:block;
}
.video-container{
    position: relative;
    overflow: hidden;
}
.video-forward-notify{
  text-align: center;
  width:30%;
  height:200%;
  border-radius:100% 0 0 100%;
  position: absolute;
  display:flex;
  flex-direction: row;
  right: 0%;
  top:-50%;
}

.video-forward-notify .icon{
  justify-content:flex-start;
  align-items:center;
  margin: auto 0 auto 40%;
  color: white;
}
.video-rewind-notify{
  text-align: center;
  width:30%;
  height:200%;
  border-radius:0 100% 100% 0;
  position: absolute;
  display:flex;
  flex-direction: row;
  left: 0;
  top:-50%;
}

.video-rewind-notify .icon{
  justify-content:flex-start;
  align-items:center;
  margin: auto 0 auto 40%;
  color: white;
}
.icon i{
  display:block;
}
.notification{
  transition: background 0.8s;
  background: rgba(200,200,200,.4) radial-gradient(circle, transparent 1%, rgba(200,200,200,.4) 1%) center/15000%;
  pointer-events:none;
  display: none;
}
i{
  font-style:normal;
}
.animate-in{
  display:flex;
  animation: ripple 1s forwards;
}
.animate-in i{
  display:block;
}
.animate-in.forward i{
  padding-bottom:2px;
}
.animate-in.forward i{
  animation: fadeInLeft .7s;
}
.animate-in.rewind i{
  animation: fadeInRight .7s;
}
@keyframes ripple{
  0%   { 
    background-color: rgba(200,200,200,.4);
    background-size: 100%;
    transition: background 0s;
    opacity:1;
  }
  100% { 
  transition: background 0.8s;
  background: rgba(200,200,200,.4) radial-gradient(circle, transparent 1%, rgba(200,200,200,.4) 1%) center/15000%;
  display: flex;
    opacity:0;
  }
}
@keyframes fadeInLeft {
  0% {
    opacity: 0;
    transform: translateX(-20px);
  }
  100% {
    opacity: 1;
    transform: translateX(0);
  }
}
@keyframes fadeInRight {
  0% {
    opacity: 0;
    transform: translateX(0px);
  }
  100% {
    opacity: 1;
    transform: translateX(-20px);
  }
}
font12{
  font-size:12px;
}
</style>

Now, do the following with the video tag that is going to be initialised with plyr:

<div class="video-container">
            <video controls playsinline autoplay id="video_player">
            </video>
            <div class="video-rewind-notify rewind notification">
                <div class="rewind-icon icon">
                    <i class="left-triangle triangle">◀◀◀</i>
                    <span class="rewind font12">10 seconds</span>
                </div>
              </div>
              <div class="video-forward-notify forward notification">
                <div class="forward-icon icon">
                    <i class="right-triangle triangle">▶▶▶</i>
                    <span class="forward font12">10 seconds</span>
                </div>
              </div>
        </div>

With the above done for video tag replacement add the following scripts to the end of body tag:

<script type="text/javascript">

//grab the video dom element
const video = document.querySelector('video'); 
const notifications = document.querySelectorAll('.notification');
const forwardNotificationValue = document.querySelector('.video-forward-notify span');
const rewindNotificationValue = document.querySelector('.video-rewind-notify span');

let timer;
let rewindSpeed = 0;
let forwardSpeed = 0;

//function for double click event listener on the video
//todo change those variable to html5 data attributes
function updateCurrentTime(delta){
    let isRewinding = delta < 0;
  
    if(isRewinding){
      rewindSpeed = rewindSpeed + delta;
      forwardSpeed = 0;
    }else{
      forwardSpeed = forwardSpeed + delta;
      rewindSpeed = 0;
    }
    
    //clear the timeout
    clearTimeout(timer);
  
    let speed = (isRewinding ? rewindSpeed : forwardSpeed);
    video.currentTime = video.currentTime + speed;
  
    let NotificationValue =  isRewinding ? rewindNotificationValue : forwardNotificationValue ;
    NotificationValue.innerHTML = `${Math.abs(speed)} seconds`;
  
    //reset accumulator within 2 seconds of a double click
    timer = setTimeout(function(){
      rewindSpeed = 0;
      forwardSpeed = 0;
    }, 2000); // you can edit this delay value for the timeout, i have it set for 2 seconds
    console.log(`updated time: ${video.currentTime}`);
}


function animateNotificationIn(isRewinding){
  isRewinding ? notifications[0].classList.add('animate-in') : notifications[1].classList.add('animate-in'); 
}

function animateNotificationOut(){
    this.classList.remove('animate-in');
}

function forwardVideo(){
  updateCurrentTime(10);
  animateNotificationIn(false);
}

function rewindVideo(){
    updateCurrentTime(-10);
    animateNotificationIn(true);
}

//Event Handlers
function doubleClickHandler(e){
    console.log(`current time: ${video.currentTime}`);
    const videoWidth = video.offsetWidth;
    (e.offsetX < videoWidth/2) ? rewindVideo() : forwardVideo();
}

function togglePlay(){
  video.paused ? video.play() : video.pause();
}

// If you want it to work on desktop browsers, just replace the condition with true
if (window.is_tablet_browser() || window.is_mobile_browser()) {
  //Event Listeners
  video.addEventListener('click', togglePlay);
  video.addEventListener('dblclick', doubleClickHandler);
  notifications.forEach(function(notification){
    notification.addEventListener('animationend', animateNotificationOut);
  });
}
        </script>

Make sure to add this utility script tag somewhere in the head tag:

<script type="text/javascript">
function is_mobile_browser () {
    let check = false;
    (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
    return check;
}

function is_tablet_browser () {
    let check = false;
    (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
    return check;
}
</script>

Now remove the doubleclick event for plyr to fullscreen so it does not interfere with our double click event to forward or rewind. Add the following just after initializing plyr:

player.eventListeners.forEach(function(eventListener) {
    if(eventListener.type === 'dblclick') {
        eventListener.element.removeEventListener(eventListener.type, eventListener.callback, eventListener.options);
    };
});

With the above modifications made. When your website is opened in a tablet or a mobile browser, the double tap to forward or rewind will be available. Note: This can work on desktop and laptops but it's disabled by default in the script. You can modify the provided script to make it work on desktops also

amk200 avatar Sep 10 '21 04:09 amk200

Okk I've done it with some css and js Limitations, one video per page (you can twick the code and by pass that

const byId = document.getElementById.bind(document),
	byClass = document.getElementsByClassName.bind(document),
	byTag = document.getElementsByTagName.bind(document),
	byName = document.getElementsByName.bind(document),
	createElement = document.createElement.bind(document);

var player = new Plyr('#player');

// Remove all dblclick stuffs
player.eventListeners.forEach(function (eventListener) {
	if (eventListener.type === 'dblclick') {
		eventListener.element.removeEventListener(eventListener.type, eventListener.callback, eventListener.options);
	}
});


// Create overlay that will show the skipped time
const skip_ol = createElement("div");
skip_ol.id = "plyr__time_skip"
byClass("plyr")[0].appendChild(skip_ol)

// A class to manage multi click count and remember last clicked side (may cause issue otherwise)

class multiclick_counter {
	constructor() {
		this.timers = []; // collection of timers. Important 
		this.count = 0; // click count
		this.reseted = 0; // before resetting what was the count
		this.last_side = null; // L C R 3sides
	}

	clicked() {
		this.count += 1
		var xcount = this.count; // will be checked if click count increased in the time
		this.timers.push(setTimeout(this.reset.bind(this, xcount), 500)); // wait till 500ms for next click

		return this.count
	}

	reset_count(n) {
		// Reset count if clicked on the different side
		this.reseted = this.count
		this.count = n
		for (var i = 0; i < this.timers.length; i++) {
			clearTimeout(this.timers[i]);
		}
		this.timer = []

	}

	reset(xcount) {
		if (this.count > xcount) { return } // return if clicked after timer started
		// Reset otherwise
		this.count = 0;
		this.last_side = null;
		this.reseted = 0;
		skip_ol.style.opacity = "0";
		this.timer = []
	}

}

var counter = new multiclick_counter();


const poster = byClass("plyr__poster")[0]
// We will target the poster since this is the only thing sits between video and controls

poster.onclick = function (e) {
	const count = counter.clicked()
	if (count < 2) { return } // if not double click

	const rect = e.target.getBoundingClientRect();
	const x = e.clientX - rect.left; //x position within the element.
	const y = e.clientY - rect.top;  //y position within the element.
	console.log("Left? : " + x + " ; Top? : " + y + ".");
	// The relative position of click on video

	const width = e.target.offsetWidth;
	const perc = x * 100 / width;

	var panic = true; // panic if the side needs to be checked
	var last_click = counter.last_side

	if (last_click == null) {
		panic = false
	}
	
        if (perc < 40) {
	  if(player.currentTime==0){
	    return // won't seek beyond 0
	  }
		counter.last_side = "L"
		if (panic && last_click != "L") {
			counter.reset_count(1)
			return
		}

		skip_ol.style.opacity = "0.9";
		player.rewind()
		skip_ol.innerText = "⫷⪡" + "\\n" + ((count - 1) * 10) + "s";

	}
	else if (perc > 60) {
	if(player.currentTime==player.duration){
    return // won't seek beyond duration 
  }
		counter.last_side = "R"
		if (panic && last_click != "R") {
			counter.reset_count(1)
			return
		}

		skip_ol.style.opacity = "0.9";
		last_click = "R"
		player.forward()
		skip_ol.innerText = "⪢⫸ " + "\n" + ((count - 1) * 10) + "s";

	}
	else {
		player.togglePlay()
		counter.last_click = "C"
	}

}

The CSS part:

#plyr__time_skip {
	background: #111111cc;
	border: 0;
	border-radius: 50%;
	color: #fff;
	left: 50%;
	min-width: 80px;
	width: min-content;
	max-width: 100px;
	max-height: 90px;
	opacity: 0;
	display: table-cell;
	text-align: center;
	vertical-align: middle;
	transform: translate(-50%, -50%);
	padding-top: 20px;
	position: absolute;
	top: 50%;
	transition: 1s;
	z-index: 3;
	pointer-events: none;
	box-shadow: 0px 0px 45px #000000;
}

Demo:

https://user-images.githubusercontent.com/34002411/192063572-eb1ed0b7-7fca-4868-a17c-5c3186ad10b9.mp4

RaSan147 avatar Sep 23 '22 21:09 RaSan147

I used @RaSan147 code for my needs with own implementation and multiple videos.

JS

for (let video of document.querySelectorAll('video')) {
    const player = new Plyr(video);

    player.on('ready', () => {
        const root = video.closest('.plyr-video');
    
        // remove double click handlers
        player.eventListeners.ForEach (function (EventListener) {
            if (eventListener.type === 'dblclick') {
                eventListener.element.removeEventListener(eventListener.type, eventListener.callback, eventListener.options);
            }
        });

        const poster = root.querySelector('.plyr__poster');
        const timeSkip = document.createElement('div');
        const resetState = () => {
            poster.clickedTimes = 0;
            poster.lastSideClicked = undefined;
        };

        timeSkip.className = 'plyr__time-skip';
        poster.parentNode.insertBefore(timeSkip, poster);
        poster.clickedTimes = 0;

        // handle clicks
        poster.addEventListener('click', function (event) {
            poster.clickedTimes++;

            if (poster.resetTimeout) {
                clearTimeout(poster.resetTimeout);
            }

            poster.resetTimeout = setTimeout(resetState, 1000);

            // handle only double click
            if (poster.clickedTimes < 2) {
                return;
            }

            // find click position
            const percentage = (event.clientX - event.target.getBoundingClientRect().left) * 100 / event.target.offsetWidth;

            if (percentage < 40) {
                if (player.currentTime === 0
                    || (typeof poster.lastSideClicked !== 'undefined' && poster.lastSideClicked !== 'L')
                ) {
                    clearTimeout(poster.resetTimeout);
                    resetState();

                    return;
                }

                timeSkip.innerText = '<<\n' + ((poster.clickedTimes - 1) * 10) + 's';
                timeSkip.classList.add('is-left');
                timeSkip.classList.remove('is-right');
                timeSkip.classList.remove('is-animated');
                setTimeout(() => timeSkip.classList.add('is-animated'), 1);
                poster.lastSideClicked = 'L';
                player.rewind();
            } else if (percentage > 60) {
                if (player.currentTime === player.duration
                    || (typeof poster.lastSideClicked !== 'undefined' && poster.lastSideClicked !== 'R')
                ) {
                    clearTimeout(poster.resetTimeout);
                    resetState();

                    return;
                }

                timeSkip.innerText = '>>\n' + ((poster.clickedTimes - 1) * 10) + 's';
                timeSkip.classList.add('is-right');
                timeSkip.classList.remove('is-left');
                timeSkip.classList.remove('is-animated');
                setTimeout(() => timeSkip.classList.add('is-animated'), 1);
                poster.lastSideClicked = 'R';
                player.forward();
            } else {
                poster.lastSideClicked = 'C';
            }
        });
    });
}

SCSS

@keyframes plyr__time-skip {
    40% {
        opacity: 1;
    }

    100% {
        opacity: 0;
    }
}

.plyr {
    &__time-skip {
        position: absolute;
        top: 0;
        bottom: 0;
        z-index: 10;
        display: flex;
        align-items: center;
        justify-content: center;
        color: #fff;
        width: 40%;
        opacity: 0;
        pointer-events: none;

        &.is-left {
            left: 0;
            background: linear-gradient(90deg, rgba(0, 0, 0, 0.5) 0%, transparent 100%);
        }

        &.is-right {
            right: 0;
            background: linear-gradient(90deg, transparent 0%, rgba(0, 0, 0, 0.5) 100%);
        }

        &.is-animated {
            animation: plyr__time-skip ease 1s forwards;
        }
    }
}

trollwinner avatar Aug 23 '23 15:08 trollwinner