{ const videoRef = useRef(null); const playerRef = useRef(null); const { link, isDual, keyword, lecture_id, is_cf_possible, getFeatureFlags, featureFlags, vod_key, version, changeSpeed, changePlayerSpeed, } = props; const { currentSpeed } = changeSpeed; const rootElement = document.querySelector(":root"); const isDualRef = useRef(isDual); useEffect(()=>{ isDualRef.current = isDual; },[isDual]) const timeout"> { const videoRef = useRef(null); const playerRef = useRef(null); const { link, isDual, keyword, lecture_id, is_cf_possible, getFeatureFlags, featureFlags, vod_key, version, changeSpeed, changePlayerSpeed, } = props; const { currentSpeed } = changeSpeed; const rootElement = document.querySelector(":root"); const isDualRef = useRef(isDual); useEffect(()=>{ isDualRef.current = isDual; },[isDual]) const timeout"> { const videoRef = useRef(null); const playerRef = useRef(null); const { link, isDual, keyword, lecture_id, is_cf_possible, getFeatureFlags, featureFlags, vod_key, version, changeSpeed, changePlayerSpeed, } = props; const { currentSpeed } = changeSpeed; const rootElement = document.querySelector(":root"); const isDualRef = useRef(isDual); useEffect(()=>{ isDualRef.current = isDual; },[isDual]) const timeout">
import React, { useState, useEffect, useRef } from "react";
import videojs from "video.js";
import "video.js/dist/video-js.css";
import "./LecPlayerV2.css";
import seekButtons from "videojs-seek-buttons";
import qualityLevels from "videojs-contrib-quality-levels";
import hlsQualitySelector from "videojs-hls-quality-selector";
import hotkeys from "videojs-hotkeys";
import { connect } from "react-redux";
import { featureActions, changePlayerSpeed } from "../../../../_state/actions";
const LecPlayerV2 = (props) => {
const videoRef = useRef(null);
const playerRef = useRef(null);
const {
link,
isDual,
keyword,
lecture_id,
is_cf_possible,
getFeatureFlags,
featureFlags,
vod_key,
version,
changeSpeed,
changePlayerSpeed,
} = props;
const { currentSpeed } = changeSpeed;
const rootElement = document.querySelector(":root");
const isDualRef = useRef(isDual);
useEffect(()=>{
isDualRef.current = isDual;
},[isDual])
const timeout = useRef();
const clickSeek = () => {
rootElement.style.setProperty("--pseudo-visibility", "visible");
clearTimeout(timeout.current);
timeout.current = setTimeout(() => {
rootElement.style.setProperty("--pseudo-visibility", "hidden");
}, 1400);
return 10;
};
function insertAfter(newEl, element) {
element.parentNode.insertBefore(newEl, element.nextSibling);
}
const speedy = (player) => {
let btn = document.createElement("button");
btn.classList.add("speedbtn");
let btndisplay = document.createElement("div");
btndisplay.innerText = `${currentSpeed}x`;
btndisplay.classList.add("speedbtn-display");
btn.appendChild(btndisplay);
let btnhover = document.createElement("div");
btnhover.classList.add("speedbtn-hover");
btn.appendChild(btnhover);
let btntitle = document.createElement("div");
btntitle.classList.add("speedbtn-title");
btntitle.append("강의 속도");
btnhover.appendChild(btntitle);
let speedOptions = [0.5, 0.75, 1.0, 1.25, 1.5, 2];
for (let i = 0; i < speedOptions.length; i++) {
let thisSpeed = speedOptions[i];
let btnli = document.createElement("li");
btnli.classList.add("speedbtn-li");
btnli.append(`${thisSpeed}배`);
btnli.onclick = () => {
changePlayerSpeed(thisSpeed);
player.playbackRate(thisSpeed);
document
.querySelector(".speedbtn-li-selected")
.classList.remove("speedbtn-li-selected");
btndisplay.innerText = `${thisSpeed}x`;
btnhover.style.visibility = "hidden";
btnli.classList.add("speedbtn-li-selected");
};
if (thisSpeed == currentSpeed) {
player.playbackRate(currentSpeed);
btnli.classList.add("speedbtn-li-selected");
}
btnhover.appendChild(btnli);
}
btn.addEventListener("mouseenter", function () {
btnhover.style.visibility = "visible";
});
btn.addEventListener("mouseleave", function () {
btnhover.style.visibility = "hidden";
});
insertAfter(btn, document.querySelector(".vjs-playback-rate"));
};
let videoJsOptions = {
// lookup the options in the docs for more options
autoplay: false,
controls: true,
crossOrigin: "anonymous",
responsive: true,
fluid: true,
inactivityTimeout: 1000,
plugins: {
seekButtons: {
forward: 10,
back: 10,
},
},
controlBar: {
durationDisplay: true,
timeDivider: true,
progressControl: true,
volumeMenuButton: true,
remainingTimeDisplay: false,
subsCapsButton: false,
fullscreenToggle: true,
},
poster: `https://video.spartacodingclub.kr/vod/courses/${vod_key}/${version}/thumbs/${lecture_id}.png`,
sources: [
{
src: `https://video.spartacodingclub.kr/vod/courses/${vod_key}/${version}/lectures/${lecture_id}.m3u8`,
// src: `https://video.spartacodingclub.kr/vod/courses/${vod_key}/${version}/lectures/${lecture_id}.mp4`,
// src: "<https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8>",
},
],
};
useEffect(() => {
// make sure Video.js player is only initialized once
if (!playerRef.current) {
const videoElement = videoRef.current;
if (!videoElement) return;
videojs.registerPlugin("hlsQualitySelector", hlsQualitySelector);
videojs.registerPlugin("speedy", speedy);
const player = (playerRef.current = videojs(
videoElement,
videoJsOptions,
() => {
console.log("player is ready");
let video = document.querySelector("video");
let vjs = document.querySelector(".video-js");
vjs.addEventListener("mouseleave", () => {
if (!video.paused) {
vjs.classList.add("vjs-user-inactive");
}
vjs.classList.remove("vjs-user-active");
});
vjs.addEventListener("mouseenter", () => {
vjs.classList.remove("vjs-user-inactive");
vjs.classList.add("vjs-user-active");
});
const progressbar = document.querySelector(".vjs-progress-holder");
const sliderbar = document.querySelector(
".vjs-play-progress.vjs-slider-bar"
);
let clicking = false;
function slide(e) {
let curwidth =
e.clientX - (window.innerWidth - progressbar.offsetWidth) / 2;
if (isDualRef.current) curwidth -= 29;
sliderbar.style.width = `${
(curwidth * 100) / progressbar.offsetWidth
}%`;
}
function touchSlide(e) {
let curwidth = e.changedTouches[0].clientX - (window.innerWidth - progressbar.offsetWidth) / 2;
if (isDualRef.current) curwidth -= 29;
sliderbar.style.width = `${(curwidth * 100) / progressbar.offsetWidth}%`;
clickSeek();
}
progressbar.addEventListener("mousedown", slide);
progressbar.addEventListener("mousedown", () => (clicking = true));
progressbar.addEventListener("mouseup", () => (clicking = false));
progressbar.addEventListener("mouseleave", () => (clicking = false));
progressbar.addEventListener(
"mousemove",
(e) => clicking && slide(e)
);
progressbar.addEventListener("touchstart", (e)=>touchSlide(e));
progressbar.addEventListener("touchmove",(e) => touchSlide(e));
player.speedy(player);
player.hotkeys({
seekStep: clickSeek,
});
let flexdiv = document.createElement("div");
flexdiv.style.width = "220em";
const tottime = document.querySelector(
".vjs-duration.vjs-time-control.vjs-control"
);
insertAfter(flexdiv, tottime);
let qualitybtn = document.createElement("button");
qualitybtn.classList.add("qualitybtn");
insertAfter(qualitybtn, document.querySelector(".speedbtn"));
let qualitybtn_display = document.createElement("div");
qualitybtn_display.innerHTML = "Auto";
qualitybtn_display.classList.add("qualitybtn-display");
qualitybtn.appendChild(qualitybtn_display);
let qualitybtn_hover = document.createElement("div");
qualitybtn_hover.classList.add("qualitybtn-hover");
qualitybtn.appendChild(qualitybtn_hover);
let qualitybtn_title = document.createElement("div");
qualitybtn_title.classList.add("qualitybtn-title");
qualitybtn_title.append("화질");
qualitybtn_hover.appendChild(qualitybtn_title);
qualitybtn.addEventListener("mouseover", function () {
qualitybtn_hover.style.visibility = "visible";
});
qualitybtn.addEventListener("mouseout", function () {
qualitybtn_hover.style.visibility = "hidden";
});
let qualityLevels = player.qualityLevels();
qualityLevels.on("change", changeOfResolution);
function changeOfResolution() {
let autobtn = document.getElementById("auto");
for (let i = 0; i < qualityLevels.length; i++) {
let btn = document.getElementById(i);
let qlevel = qualityLevels[i];
if (qlevel.height < 360) continue;
let desc = qlevel.height + "p";
let classname = "qualitybtn-li";
if (
i == qualityLevels.selectedIndex &&
autobtn &&
autobtn.className != "qualitybtn-li-selected"
) {
classname = "qualitybtn-li-selected";
qualitybtn_display.innerHTML = desc;
}
if (btn) btn.setAttribute("class", classname);
else addButton(i, desc, classname);
}
if (!autobtn) {
addButton("auto", "Auto", "qualitybtn-li-selected");
document
.getElementById("auto")
.addEventListener("click", requestAuto);
}
qualitybtn_hover.style.visibility = "hidden";
}
function addButton(bid, desc, classname) {
let btn = document.createElement("div");
document.querySelector(".qualitybtn-hover").appendChild(btn);
btn.addEventListener("click", requestResolution);
btn.innerHTML = desc;
btn.setAttribute("id", bid);
btn.setAttribute("class", classname);
}
function requestAuto() {
for (var i = 0; i < qualityLevels.length; i++) {
qualityLevels[i].enabled = true;
let btn = document.getElementById(i);
btn && btn.setAttribute("class", "qualitybtn-li");
}
document
.getElementById("auto")
.setAttribute("class", "qualitybtn-li-selected");
qualitybtn_display.innerHTML = "Auto";
}
function requestResolution() {
document
.getElementById("auto")
.setAttribute("class", "qualitybtn-li");
for (let i = 0; i < qualityLevels.length; i++) {
qualityLevels[i].enabled = this.id == i;
}
}
let capsbtn = document.createElement("button");
capsbtn.classList.add("capsbtn");
capsbtn.title = "자막 해제";
insertAfter(capsbtn, document.querySelector(".qualitybtn"));
let capsbtn_img = document.createElement("img");
capsbtn_img.classList.add("capsbtn_img");
capsbtn_img.style.position = "relative";
capsbtn_img.style.top = "-1px";
capsbtn_img.src = require("../assets/capsbtnon.svg");
capsbtn.appendChild(capsbtn_img);
capsbtn.addEventListener("click", function () {
let texttrack = document.querySelector(".vjs-text-track-display");
if (texttrack.style.visibility == "hidden") {
texttrack.style.visibility = "visible";
capsbtn.title = "자막 해제";
capsbtn_img.src = require("../assets/capsbtnon.svg");
} else {
texttrack.style.visibility = "hidden";
capsbtn.title = "자막";
capsbtn_img.src = require("../assets/capsbtnoff.svg");
}
});
let bigplaybtn = document.querySelector(".vjs-big-play-button");
const bigplaytitle = () => {
setTimeout(() => {
bigplaybtn.title = "재생";
}, 1);
};
bigplaybtn.addEventListener("mouseover", bigplaytitle);
bigplaybtn.addEventListener("click", bigplaytitle);
let playbtn = document.querySelector(".vjs-play-control");
const playtitle = () => {
setTimeout(() => {
if (playbtn.classList.value.includes("vjs-playing")) {
playbtn.title = "일시정지";
} else playbtn.title = "재생";
}, 1);
};
playbtn.addEventListener("mouseover", playtitle);
playbtn.addEventListener("click", playtitle);
let ctrlbar = document.querySelector(".vjs-control-bar");
ctrlbar.addEventListener("mouseenter", (evt) => {
let skipback = document.querySelector(
"div.vjs-control-bar > button.vjs-seek-button.skip-back.skip-10.vjs-control.vjs-button"
);
if (skipback) skipback.title = "10초 전";
let skipforward = document.querySelector(
"div.vjs-control-bar > button.vjs-seek-button.skip-forward.skip-10.vjs-control.vjs-button"
);
if (skipforward) skipforward.title = "10초 후";
});
let volumebtn = document.querySelector(".vjs-mute-control");
const volumetitle = () => {
setTimeout(() => {
if (volumebtn.classList.value.includes("vjs-vol-0")) {
volumebtn.title = "음소거 해제";
} else volumebtn.title = "음소거";
}, 1);
};
volumebtn.addEventListener("mouseover", volumetitle);
volumebtn.addEventListener("click", volumetitle);
let pipbtn = document.querySelector(
".vjs-picture-in-picture-control"
);
const piptitle = () => {
setTimeout(() => {
if (vjs.classList.value.includes("vjs-picture-in-picture")) {
pipbtn.title = "pip 모드 종료";
} else {
pipbtn.title = "pip 모드";
}
}, 1);
};
pipbtn.addEventListener("mouseover", piptitle);
pipbtn.addEventListener("click", piptitle);
let fullbtn = document.querySelector(".vjs-fullscreen-control");
const fulltitle = () => {
setTimeout(() => {
if (vjs.classList.value.includes("vjs-fullscreen")) {
fullbtn.title = "전체화면 종료";
} else {
fullbtn.title = "전체화면";
}
}, 1);
};
fullbtn.addEventListener("mouseover", fulltitle);
fullbtn.addEventListener("click", fulltitle);
player.on("waiting", () => {
console.log("player is waiting");
});
player.on("dispose", () => {
console.log("player will dispose");
});
player.on("click", (evt) => {
if (evt.srcElement.offsetParent.classList[2] === "skip-10") {
clickSeek();
}
});
}
));
} else {
// you can update player here [update player through props]
// const player = playerRef.current;
// player.autoplay(options.autoplay);
// player.src(options.sources);
}
}, [videoJsOptions, videoRef]);
// Dispose the Video.js player when the functional component unmounts
useEffect(() => {
const player = playerRef.current;
return () => {
if (player) {
player.dispose();
playerRef.current = null;
}
};
}, [playerRef]);
return (
<div className={isDual ? "dual" : "lec-player"}>
<video ref={videoRef} className={`video-js`}>
<track
label="자막"
kind="captions"
srcLang="kr"
src={`https://video.spartacodingclub.kr/vod/courses/${vod_key}/${version}/subtitles/${lecture_id}.vtt`}
default
/>
</video>
</div>
);
};
function mapStateToProps(state) {
const { featureFlags, changeSpeed } = state;
return {
featureFlags,
changeSpeed,
};
}
const connectedLecPlayerV2 = connect(mapStateToProps, {
getFeatureFlags: featureActions.getFeatureFlags,
changePlayerSpeed: changePlayerSpeed,
})(LecPlayerV2);
export { connectedLecPlayerV2 as LecPlayerV2 };
:root{
--pseudo-visibility: hidden;
--white-filter : brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(0%) hue-rotate(125deg) brightness(103%) contrast(103%);
}
.lec-player {
max-width: 1020px;
min-width: 500px;
height: 574px;
margin: 0 auto;
}
@media (max-width: 1024px){
.lec-player{
max-width: 890px;
}
}
.dual {
max-width: 1272px;
margin: 0 auto;
width: 96%;
min-width: 558px;
position:relative;
padding-left: 58px;
}
.video-js .vjs-big-play-button,
.video-js:hover .vjs-big-play-button{
width: 95px;
height: 95px;
border: none;
border-radius: 60px;
background-color: rgba(0, 0, 0, 0.8);
top: 50%;
left: 50%;
margin-top: -1.65em;
margin-left: -1.5em;
}
.video-js .vjs-big-play-button:hover {
background-color: #E8344E;
}
.vjs-paused .vjs-big-play-button{
display: block;
}
.video-js .vjs-big-play-button .vjs-icon-placeholder:before {
content : url("../assets/bigplaybtn.svg");
top: 25px;
left: 5px;
}
.video-js .vjs-control{
width: 24px;
}
.video-js .vjs-control-bar{
height: 32px;
background-color: rgba(0, 0, 0, 0.7);
/*display: flex;*/
}
.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar{
transition: visibility 0.1s, opacity 0.1s;
}
.video-js .vjs-control-bar .vjs-button:hover{
filter : var(--white-filter);
}
.video-js .vjs-progress-control {
position: absolute;
width: 100%;
height: 4px;
bottom: 32px;
background-color: rgba(0, 0, 0, 0.7);
}
.video-js .vjs-progress-control:hover{
height: 6px;
}
.video-js .vjs-progress-control:hover > .vjs-progress-holder .vjs-play-progress:before{
font-size: 14px;
visibility: visible;
}
.video-js .vjs-progress-control .vjs-progress-holder{
margin: 0;
height: 100%;
}
.video-js .vjs-play-progress {
background-color : #e8344e;
}
.video-js .vjs-play-progress:before {
color : #e8344e;
font-size: 12px;
visibility: var(--pseudo-visibility);
}
.video-js .vjs-progress-control .vjs-time-tooltip{
width: 43px;
height: 26px;
padding: 2px 6px;
background-color: rgba(0, 0, 0, 0.7);
font-family: Helvetica;
font-size: 12px;
line-height: 1.83;
border-radius : 0;
top: -31px;
}
.video-js .vjs-progress-control:hover .vjs-time-tooltip,
.video-js .vjs-progress-control:hover .vjs-progress-holder:focus .vjs-time-tooltip {
font-size: 12px;
}
.video-js .vjs-progress-holder .vjs-play-progress .vjs-time-tooltip {
display: none;
}
.video-js .vjs-load-progress div{
background-color: rgba(255, 255, 255, 0.6);
}
.video-js .vjs-slider{
background-color: rgba(255, 255, 255, 0.4);
}
.video-js .vjs-play-control {
margin-left: 12px;
margin-right: 4px;
outline: none !important;
}
.video-js .vjs-play-control .vjs-icon-placeholder:before {
content: url("../assets/smallplaybtn.svg");
top: -3px;
}
.vjs-icon-replay:before,
.video-js .vjs-play-control.vjs-ended .vjs-icon-placeholder:before{
top: 0px;
}
.video-js .vjs-play-control.vjs-playing .vjs-icon-placeholder:before {
content: url("../assets/smallstopbtn.svg");
top: -3px;
}
.video-js.vjs-v7 .vjs-seek-button.skip-back.skip-10,
.video-js.vjs-v7 .vjs-seek-button.skip-forward.skip-10 {
margin-left: 4px;
}
.video-js.vjs-v7 .vjs-seek-button.skip-back.skip-10 .vjs-icon-placeholder::before{
-webkit-transform: none;
content: url("../assets/seekbutton_back.svg");
position: relative;
top : -3px;
cursor: pointer;
margin-right: 4px;
}
.video-js.vjs-v7 .vjs-seek-button.skip-forward.skip-10 .vjs-icon-placeholder::before{
-webkit-transform: none;
content: url("../assets/seekbutton_front.svg");
position: relative;
top : -3px;
cursor: pointer;
}
.video-js .vjs-current-time,
.video-js .vjs-duration {
display: block;
}
.video-js .vjs-time-control {
font-size: 12px;
line-height: 27px;
letter-spacing: 0.12px;
color: #e5e5e5;
}
.video-js .vjs-time-divider{
display: block;
padding:0;
min-width: 0;
padding-left: 22px;
}
.video-js .vjs-remaining-time{
display:none;
}
.video-js .vjs-text-track-display{
visibility: hidden;
}
.video-js.vjs-has-started .vjs-text-track-display{
visibility: visible;
}
.video-js .vjs-text-track-display > div > div {
/*max-width: 90%;*/
padding-left: 1em;
padding-right:1em;
/*height: 34px;*/
}
.video-js .vjs-text-track-display > div > div > div{
display: inline-block !important;
font-size: 17px;
font-weight: 600;
padding: 4px 10px;
border-radius: 4px;
background-color: rgba(0, 0, 0, 0.8) !important;
line-height: 1.5;
font-family: Pretendard !important;
}
.speedbtn {
width: 40px;
cursor: pointer;
margin-left: 4px;
}
.speedbtn-display{
font-size: 12px;
font-weight: 600;
font-family : Pretendard;
line-height: 32px;
text-align: center;
letter-spacing: 0.36px;
color: #c7c9cb;
}
.speedbtn-hover{
width : 79px;
height : 177px;
display: flex;
flex-direction : column;
justify-content : space-between;
font-size : 13px;
font-family : Pretendard;
font-weight : 500;
background-color : rgba(0, 0, 0, 0.9);
position : relative;
top : -210px;
left : -25px;
border-radius : 4px;
padding-top : 10px;
padding-bottom : 10px;
visibility: hidden;
}
.speedbtn-title {
height: 19px;
font-weight: 800;
}
.speedbtn-li{
height : 23px;
text-align : left;
line-height : 23px;
padding-left : 14px;
}
.speedbtn-li:not(.speedbtn-li-selected):hover {
background-color: rgba(255, 255, 255, 0.2);
}
.speedbtn-li-selected {
background-color: white;
color : black;
}
.qualitybtn {
width: 52px;
cursor: pointer;
padding-left: 0px;
}
.qualitybtn-display{
font-size: 12px;
font-weight: 600;
font-family : Pretendard;
line-height: 32px;
text-align: center;
color: #c7c9cb;
letter-spacing: 0.36px;
}
.qualitybtn-hover{
width : 65px;
height : 177px;
/*display: flex;*/
/*flex-direction : column;*/
justify-content : space-between;
font-size : 13px;
font-family : Pretendard;
font-weight : 500;
background-color : rgba(0, 0, 0, 0.9);
position : relative;
top : -210px;
left : -12px;
border-radius : 4px;
padding-top : 10px;
padding-bottom : 10px;
visibility: hidden;
}
.qualitybtn-title {
height: 19px;
font-weight: 800;
padding-left : 10px;
text-align : left;
}
.qualitybtn-li {
height : 23px;
text-align : left;
line-height : 23px;
padding-left : 10px;
font-weight: 500;
}
.qualitybtn-li-selected {
height : 23px;
text-align : left;
line-height : 23px;
padding-left : 10px;
font-weight: 500;
background-color: white;
color: black;
}
.qualitybtn-li:hover {
background-color: rgba(255, 255, 255, 0.2);
}
.capsbtn {
width : 40px;
height: 32px;
cursor: pointer;
margin-right: 6px;
}
.capsbtn_img:hover{
filter : var(--white-filter);
}
.video-js .vjs-picture-in-picture-control{
margin-right: 6px;
}
.video-js .vjs-picture-in-picture-control .vjs-icon-placeholder:before{
content: url("../assets/pipon.svg");
top: -2px;
}
.video-js.vjs-picture-in-picture .vjs-picture-in-picture-control .vjs-icon-placeholder:before{
content: url("../assets/pipoff.svg");
top: -2px;
}
.video-js .vjs-fullscreen-control {
margin-right: 12px;
}
.video-js .vjs-fullscreen-control .vjs-icon-placeholder:before {
content: url("../assets/fullscreenon.svg");
top : -2px;
}
.video-js.vjs-fullscreen .vjs-fullscreen-control .vjs-icon-placeholder:before {
content: url("../assets/fullscreenoff.svg");
top : -2px;
}
.video-js .vjs-mute-control.vjs-vol-0 .vjs-icon-placeholder:before {
content: url("../assets/volumeoff.svg");
top: -2px;
}
.video-js .vjs-mute-control.vjs-vol-1 .vjs-icon-placeholder:before,
.video-js .vjs-mute-control.vjs-vol-2 .vjs-icon-placeholder:before,
.video-js .vjs-mute-control .vjs-icon-placeholder:before {
content: url("../assets/volumeon.svg");
top: -2px;
}
.video-js .vjs-volume-panel,
.video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-hover,
.video-js .vjs-volume-panel.vjs-volume-panel-horizontal:active,
.video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active {
width: 10em;
}
.video-js .vjs-volume-panel .vjs-volume-control,
.video-js .vjs-volume-panel.vjs-hover .vjs-volume-control.vjs-volume-horizontal,
.video-js .vjs-volume-panel:active .vjs-volume-control.vjs-volume-horizontal,
.video-js .vjs-volume-panel:focus .vjs-volume-control.vjs-volume-horizontal,
.video-js .vjs-volume-panel .vjs-volume-control:active.vjs-volume-horizontal,
.video-js .vjs-volume-panel.vjs-hover .vjs-mute-control ~ .vjs-volume-control.vjs-volume-horizontal,
.video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active.vjs-volume-horizontal {
width: 8em;
height: 3em;
opacity: 1;
margin-right: 0;
}
.vjs-slider-horizontal .vjs-volume-level:before{
display: none;
}
.vjs-volume-bar.vjs-slider-bar,
.vjs-volume-level {
border-radius: 10px;
width: 8em;
}
div.vjs-volume-panel.vjs-control.vjs-volume-panel-horizontal{
order : 1;
}
.speedbtn{
order: 2;
}
.qualitybtn {
order: 3;
}
.capsbtn {
order: 4;
}
.vjs-picture-in-picture-control {
order : 5;
}
.vjs-fullscreen-control {
order: 6;
}
import React, { useState, useEffect, useRef } from "react";
import videojs from "video.js";
import "video.js/dist/video-js.css";
import "./LecPlayerV2mobile.scss";
import seekButtons from "videojs-seek-buttons";
import qualityLevels from "videojs-contrib-quality-levels";
import hlsQualitySelector from "videojs-hls-quality-selector";
import 'videojs-landscape-fullscreen'
import hotkeys from "videojs-hotkeys";
import { connect } from "react-redux";
import { featureActions, changePlayerSpeed } from "../../../../_state/actions";
const LecPlayerV2mobile = (props) => {
const videoRef = useRef(null);
const playerRef = useRef(null);
const {
link,
isDual,
keyword,
lecture_id,
is_cf_possible,
getFeatureFlags,
featureFlags,
vod_key,
version,
changeSpeed,
changePlayerSpeed,
} = props;
const rootElement = document.querySelector(":root");
const timeout = useRef();
const skipTimeOut = useRef();
const clickSeek = () => {
rootElement.style.setProperty("--pseudo-visibility", "visible");
clearTimeout(timeout.current);
timeout.current = setTimeout(() => {
rootElement.style.setProperty("--pseudo-visibility", "hidden");
}, 1400);
return 10;
};
function insertAfter(newEl, element) {
element.parentNode.insertBefore(newEl, element.nextSibling);
}
let videoJsOptions = {
// lookup the options in the docs for more options
autoplay: false,
controls: true,
crossOrigin: "anonymous",
responsive: true,
fluid: true,
inactivityTimeout: 1000,
plugins: {
seekButtons: {
forward: 10,
back: 10,
},
},
controlBar: {
durationDisplay: true,
timeDivider: true,
progressControl: true,
volumeMenuButton: true,
remainingTimeDisplay: false,
subsCapsButton: false,
fullscreenToggle: true,
},
poster: `https://video.spartacodingclub.kr/vod/courses/${vod_key}/${version}/thumbs/${lecture_id}.png`,
sources: [
{
src: `https://video.spartacodingclub.kr/vod/courses/${vod_key}/${version}/lectures/${lecture_id}.m3u8`,
// src: `https://video.spartacodingclub.kr/vod/courses/${vod_key}/${version}/lectures/${lecture_id}.mp4`,
// src: "<https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8>",
}
],
};
useEffect(() => {
// make sure Video.js player is only initialized once
if (!playerRef.current) {
const videoElement = videoRef.current;
if (!videoElement) return;
videojs.registerPlugin("hlsQualitySelector", hlsQualitySelector);
const player = (playerRef.current = videojs(
videoElement,
videoJsOptions,
() => {
console.log("player is ready");
let video = document.querySelector("video");
let vjs = document.querySelector(".video-js");
vjs.addEventListener("mouseleave", () => {
if (!video.paused) {
vjs.classList.add("vjs-user-inactive");
}
vjs.classList.remove("vjs-user-active");
});
vjs.addEventListener("mouseenter", () => {
vjs.classList.remove("vjs-user-inactive");
vjs.classList.add("vjs-user-active");
});
let overlayDiv = document.createElement("div");
overlayDiv.classList.add("overlayDiv");
vjs.appendChild(overlayDiv);
let skipCircleContainer_R = document.createElement('div');
skipCircleContainer_R.classList.add("skipCircleContainer_R");
vjs.appendChild(skipCircleContainer_R);
let skipCircle_R = document.createElement('div');
skipCircle_R.classList.add("skipCircle_R");
skipCircleContainer_R.appendChild(skipCircle_R);
let skipCircleIcon_R = document.createElement("img");
skipCircleIcon_R.src = require('../mobile_assets/10seconds_after.svg');
skipCircle_R.appendChild(skipCircleIcon_R);
let skipTouch_R = document.createElement('div');
skipTouch_R.classList.add("skipTouch_R");
let touchtime = 0;
skipTouch_R.addEventListener("touchstart", ()=>{
if (touchtime == 0){
touchtime = new Date().getTime();
}
else{
if (((new Date().getTime()) - touchtime) < 600) {
playerRef.current.currentTime(playerRef.current.currentTime() + 10);
touchtime = 0;
skipCircle_R.style.visibility = "visible";
clearTimeout(skipTimeOut.current);
skipTimeOut.current = setTimeout(() => {
skipCircle_R.style.visibility = "hidden";
}, 600);
} else {
touchtime = 0;
}
}
})
skipCircle_R.appendChild(skipTouch_R);
let skipCircleContainer_L = document.createElement('div');
skipCircleContainer_L.classList.add("skipCircleContainer_L");
vjs.appendChild(skipCircleContainer_L);
let skipCircle_L = document.createElement('div');
skipCircle_L.classList.add("skipCircle_L");
skipCircleContainer_L.appendChild(skipCircle_L);
let skipCircleIcon_L = document.createElement("img");
skipCircleIcon_L.src = require('../mobile_assets/10seconds_before.svg');
skipCircle_L.appendChild(skipCircleIcon_L);
let skipTouch_L = document.createElement('div');
skipTouch_L.classList.add("skipTouch_L");
skipTouch_L.addEventListener("touchstart", ()=>{
if (touchtime == 0){
touchtime = new Date().getTime();
}
else{
if (((new Date().getTime()) - touchtime) < 600) {
playerRef.current.currentTime(playerRef.current.currentTime() - 10);
touchtime = 0;
skipCircle_L.style.visibility = "visible";
clearTimeout(skipTimeOut.current);
skipTimeOut.current = setTimeout(() => {
skipCircle_L.style.visibility = "hidden";
}, 600);
} else {
touchtime = 0;
}
}
})
skipCircle_L.appendChild(skipTouch_L);
const progressbar = document.querySelector(".vjs-progress-holder");
const sliderbar = document.querySelector(
".vjs-play-progress.vjs-slider-bar"
);
function slide(e) {
let curwidth = e.changedTouches[0].clientX - (window.innerWidth - progressbar.offsetWidth) / 2;
sliderbar.style.width = `${(curwidth * 100) / progressbar.offsetWidth}%`;
clickSeek();
}
progressbar.addEventListener("touchstart", (e)=>slide(e));
progressbar.addEventListener("touchmove",(e) => slide(e));
let pc = document.querySelector(".vjs-progress-control");
let cb = document.querySelector(".vjs-control-bar");
insertAfter(pc, cb);
player.hotkeys({
seekStep: clickSeek,
});
player.landscapeFullscreen({
fullscreen: {
enterOnRotate: true,
exitOnRotate: true,
alwaysInLandscapeMode: true,
iOS: false,
}
})
let flexdiv = document.createElement("div");
flexdiv.style.width = "220em";
const tottime = document.querySelector(
".vjs-duration.vjs-time-control.vjs-control"
);
insertAfter(flexdiv, tottime);
let capsbtn = document.createElement("button");
capsbtn.classList.add("capsbtn");
capsbtn.title = "자막 해제";
insertAfter(capsbtn, document.querySelector(".vjs-playback-rate"));
let capsbtn_img = document.createElement("img");
capsbtn_img.classList.add("capsbtn_img");
capsbtn_img.style.position = "relative";
capsbtn_img.style.top = "-1px";
capsbtn_img.src = require("../assets/capsbtnon.svg");
capsbtn.appendChild(capsbtn_img);
capsbtn.addEventListener("click", () => changeCurSubtitle());
let settingbtn = document.createElement("img");
settingbtn.classList.add("settingbtn");
settingbtn.src = require("../mobile_assets/settingbtn.svg");
settingbtn.addEventListener("click", function () {
if (vjs.classList.value.includes("vjs-fullscreen")){
if (fullscreenModal.classList.value.includes('open')){
fullscreenModal.classList.add('closed');
fullscreenModal.classList.remove('open');
}
else{
fullscreenModal.classList.remove('closed');
fullscreenModal.classList.add('open');
}
// return
}
[-2,-1].includes(ModalOnRef.current) ? changeModalOn(0) : changeModalOn(-1);
});
insertAfter(settingbtn, capsbtn);
function makeExitBtn(){
let exitBtn = document.createElement("img");
exitBtn.classList.add("exitBtn");
exitBtn.src = require("../mobile_assets/x.svg");
exitBtn.addEventListener('click', () => {
fullscreenModal.classList.add('closed');
fullscreenModal.classList.remove('open');
changeModalOn(-1);
});
return exitBtn;
}
function makeBackBtn(){
let backBtn = document.createElement("img");
backBtn.classList.add("backBtn");
backBtn.src = require("../mobile_assets/return.svg");
backBtn.style.marginRight = "17px";
backBtn.addEventListener('click', () => {
changeModalOn(-1);
});
return backBtn;
}
let fullscreenModal = document.createElement("div");
fullscreenModal.classList.add("fullscreenModal");
let header = document.createElement('div');
header.classList.add("header");
header.innerHTML = '영상설정';
fullscreenModal.appendChild(header);
fullscreenModal.appendChild(makeExitBtn());
let menuBar = document.createElement('div');
menuBar.classList.add('menu-bar');
fullscreenModal.appendChild(menuBar);
function makeOptionCurrent(idx){
let returnDiv = document.createElement('div');
switch(idx){
case 1:
returnDiv.innerHTML = curSpeed == 1 ? "기본" : curSpeed + "배";
break;
case 2:
returnDiv.innerHTML = SubtitleRef.current;
break;
case 3:
returnDiv.innerHTML = curPip;
break
case 4:
returnDiv.innerHTML = curQuality;
break
}
return returnDiv;
}
function makeModalElements(idx, img, title) {
let optionDiv = document.createElement('div');
optionDiv.addEventListener('click', () => changeModalOn(idx));
let optionImg = document.createElement('img');
optionImg.src = require(`../mobile_assets/${img}.svg`);
let optionTitle = document.createElement('div');
optionTitle.innerHTML = title;
let optionCurrent = makeOptionCurrent(idx);
optionDiv.appendChild(optionImg);
optionDiv.appendChild(optionTitle);
optionDiv.appendChild(optionCurrent);
return optionDiv;
}
menuBar.appendChild(makeModalElements(1, "speedOption","재생속도"));
menuBar.appendChild(makeModalElements(2, "subtitleOption","자막"));
menuBar.appendChild(makeModalElements(3, "pipOption","PIP 모드"));
menuBar.appendChild(makeModalElements(4, "qualityOption","화질"));
function makeOptionModal(idx) {
let optionItem = settingOptions[idx];
let optionElement = document.createElement('div');
optionElement.classList.add("fullscreenModal", "modalSetting__options", `${idx}`);
optionElement.appendChild(makeExitBtn());
let header = document.createElement('div');
header.classList.add("header");
// header.innerHTML = optionItem.title;
optionElement.appendChild(header);
header.appendChild(makeBackBtn());
let headerTitle = document.createElement("span");
headerTitle.innerHTML = optionItem.title;
header.appendChild(headerTitle);
let menuBar = document.createElement('div');
menuBar.classList.add('menu-bar');
optionItem.options.map((item) =>{
let itemDiv = document.createElement('div');
itemDiv.style.color = optionItem.selectedOption == item ? "#E8344E" : "";
itemDiv.addEventListener('click', () => optionItem.onClick(item));
itemDiv.innerHTML = item == "Auto" ? item : item + optionItem.tag;
menuBar.appendChild(itemDiv);
})
optionElement.appendChild(menuBar);
return optionElement;
}
insertAfter(makeOptionModal(1), cb)
insertAfter(makeOptionModal(2), cb)
insertAfter(makeOptionModal(3), cb)
insertAfter(makeOptionModal(4), cb)
insertAfter(fullscreenModal, cb)
player.on("waiting", () => {
console.log("player is waiting");
});
player.on("dispose", () => {
console.log("player will dispose");
});
player.on("click", (evt) => {
if (evt.srcElement.offsetParent.classList[2] === "skip-10") {
clickSeek();
}
});
}
));
} else {
// you can update player here [update player through props]
// const player = playerRef.current;
// player.autoplay(options.autoplay);
// player.src(options.sources);
}
}, [videoJsOptions, videoRef]);
// Dispose the Video.js player when the functional component unmounts
useEffect(() => {
const player = playerRef.current;
return () => {
if (player) {
player.dispose();
playerRef.current = null;
}
};
}, [playerRef]);
const [curSpeed, setCurSpeed] = useState(1);
const [curSubtitle, setCurSubtitle] = useState("ON");
const SubtitleRef = useRef(curSubtitle);
const [curPip, setCurPip] = useState("OFF");
const [curQuality, setCurQuality] = useState("Auto");
const changeCurSpeed = (idx) => {
playerRef.current.playbackRate(idx);
setCurSpeed(idx);
changePlayerSpeed(idx);
let menuBar = document.querySelector(".fullscreenModal.modalSetting__options.\\\\31 > .menu-bar");
if (!menuBar) return;
for (let i= 0; i < menuBar.childNodes.length; i++){
if (menuBar.childNodes[i].innerHTML == idx+"배") menuBar.childNodes[i].style.color = "#E8344E";
else menuBar.childNodes[i].style.color = "black";
}
document.querySelector(".fullscreenModal > .menu-bar > div:nth-child(1) > div:nth-child(3)").innerHTML =
idx == 1 ? "기본" : idx + "배";
};
const changeCurSubtitle = () => {
let texttrack = document.querySelector(".vjs-text-track-display");
let capsbtn_img = document.querySelector(".capsbtn_img");
if (SubtitleRef.current == "ON") {
texttrack.style.visibility = "hidden";
capsbtn_img.src = require("../assets/capsbtnoff.svg");
setCurSubtitle("OFF");
SubtitleRef.current = "OFF";
document.querySelector(".fullscreenModal > .menu-bar > div:nth-child(2) > div:nth-child(3)").innerHTML = "OFF";
let menuBar = document.querySelector(".fullscreenModal.modalSetting__options.\\\\32 > .menu-bar");
if (!menuBar) return;
for (let i= 0; i < menuBar.childNodes.length; i++){
if (menuBar.childNodes[i].innerHTML == "OFF") menuBar.childNodes[i].style.color = "#E8344E";
else menuBar.childNodes[i].style.color = "black";
}
} else {
texttrack.style.visibility = "visible";
capsbtn_img.src = require("../assets/capsbtnon.svg");
setCurSubtitle("ON");
SubtitleRef.current = "ON";
document.querySelector(".fullscreenModal > .menu-bar > div:nth-child(2) > div:nth-child(3)").innerHTML = "ON";
let menuBar = document.querySelector(".fullscreenModal.modalSetting__options.\\\\32 > .menu-bar");
if (!menuBar) return;
for (let i= 0; i < menuBar.childNodes.length; i++){
if (menuBar.childNodes[i].innerHTML == "ON") menuBar.childNodes[i].style.color = "#E8344E";
else menuBar.childNodes[i].style.color = "black";
}
}
};
const changeCurPiP = async (idx) => {
setCurPip(idx);
if (curPip == idx) return;
if (idx == "ON") {
let success = await playerRef.current.requestPictureInPicture();
if (success == undefined){
alert('pip 모드를 지원하지 않습니다');
setCurPip("OFF");
changeModalOn(-1);
document.querySelector(".fullscreenModal.modalSetting__options.\\\\33").classList.remove("open");
document.querySelector(".fullscreenModal").classList.add('closed');
document.querySelector(".fullscreenModal").classList.remove('open');
return;
};
}
else {
await playerRef.current.exitPictureInPicture();
}
let menuBar = document.querySelector(".fullscreenModal.modalSetting__options.\\\\33 > .menu-bar");
if (!menuBar) return;
for (let i= 0; i < menuBar.childNodes.length; i++){
if (menuBar.childNodes[i].innerHTML == idx) menuBar.childNodes[i].style.color = "#E8344E";
else menuBar.childNodes[i].style.color = "black";
}
document.querySelector(".fullscreenModal > .menu-bar > div:nth-child(3) > div:nth-child(3)").innerHTML = idx;
if (idx == "ON") {
changeModalOn(-1);
document.querySelector(".fullscreenModal.modalSetting__options.\\\\33").classList.remove("open");
document.querySelector(".fullscreenModal").classList.add('closed');
document.querySelector(".fullscreenModal").classList.remove('open');
setCurPip(idx);
}
};
useEffect(() => {
videoRef.current.addEventListener("leavepictureinpicture", () => {
changeCurPiP("OFF");
})
},[videoRef]);
const changeCurQuality = (idx) => {
let qualityLevels = playerRef.current.qualityLevels();
for (let i = 0; i < qualityLevels.length; i++) {
if (idx == "Auto") qualityLevels[i].enabled = true;
else qualityLevels[i].enabled = qualityLevels[i].height == idx;
}
setCurQuality(idx);
let menuBar = document.querySelector(".fullscreenModal.modalSetting__options.\\\\34 > .menu-bar");
if (!menuBar) return;
for (let i= 0; i < menuBar.childNodes.length; i++){
if (menuBar.childNodes[i].innerHTML == "Auto" && idx == "Auto") menuBar.childNodes[i].style.color = "#E8344E";
else if (menuBar.childNodes[i].innerHTML == idx + "p") menuBar.childNodes[i].style.color = "#E8344E";
else menuBar.childNodes[i].style.color = "black";
}
document.querySelector(".fullscreenModal > .menu-bar > div:nth-child(4) > div:nth-child(3)").innerHTML =
idx == "Auto" ? idx : idx+"p";
};
let settingOptions = {
1: {
title: "재생속도",
selectedOption: curSpeed,
options: [0.5, 0.75, 1, 1.25, 1.5, 2],
tag: "배",
onClick: changeCurSpeed,
},
2: {
title: "자막",
selectedOption: curSubtitle,
options: ["ON", "OFF"],
tag: "",
onClick: changeCurSubtitle,
},
3: {
title: "PIP",
selectedOption: curPip,
options: ["ON", "OFF"],
tag: "",
onClick: changeCurPiP,
},
4: {
title: "화질",
selectedOption: curQuality,
options: [360, 540, 720, 1080, "Auto"],
tag: "p",
onClick: changeCurQuality,
},
};
// -2: initial state, -1: off, 0: on default, 1: 재생속도, 2: 자막, 3: pip, 4: 화질
const [isModalOn, setIsModalOn] = useState(-2);
const ModalOnRef = useRef(isModalOn);
const changeModalOn = (idx) => {
ModalOnRef.current = idx;
setIsModalOn(idx);
if (! document.querySelector(".video-js").classList.value.includes("vjs-fullscreen")) return;
let optionDivs = document.querySelectorAll(".fullscreenModal, .modalSetting__Options");
for (let i = 1; i < optionDivs.length; i++){
if (optionDivs[i].classList.value.includes(idx)){
optionDivs[i].classList.add("open");
}
else{
optionDivs[i].classList.remove("open");
}
}
};
const optionsElement = (index) => {
let curOption = settingOptions[index];
return (
<div className="modalSetting__options">
<div className="header">{curOption.title}</div>
<div className="menu-bar" style={{ gap: "17px" }}>
{curOption.options.map((item) => {
return (
<div
style={{
color: curOption.selectedOption == item ? "#E8344E" : "",
}}
key={item}
onClick={() => curOption.onClick(item)}
>
{item}
{item != "Auto" && curOption.tag}
</div>
);
})}
</div>
<div
className="footer"
onClick={() => {
changeModalOn(0);
}}
>
<hr />
<div className="footer__container">
<div className="footer__li">
<img src={require("../mobile_assets/return.svg")} />
<div>뒤로</div>
</div>
</div>
</div>
</div>
);
};
return (
<div className={"lec-player-mobile"}>
<video ref={videoRef} className={`video-js`}>
<track
label="자막"
kind="captions"
srcLang="kr"
src={`https://video.spartacodingclub.kr/vod/courses/${vod_key}/${version}/subtitles/${lecture_id}.vtt`}
default
/>
</video>
{[1,2,3,4].includes(isModalOn)&& optionsElement(isModalOn)}
<div className={`modalSetting ${isModalOn == -2 ? "" : isModalOn != -1 ? "open" : "closed"}`}>
<div className="header">영상설정</div>
<div className="menu-bar">
<div
className="speedOption"
onClick={() => {
changeModalOn(1);
}}
>
<img src={require("../mobile_assets/speedOption.svg")}/>
<div>재생속도</div>
<div>{curSpeed == 1 ? "기본" : curSpeed + "배"}</div>
</div>
<div
className="subtitleOption"
onClick={() => {
changeModalOn(2);
}}
>
<img src={require("../mobile_assets/subtitleOption.svg")}/>
<div>자막</div>
<div>{curSubtitle}</div>
</div>
<div
className="pipOption"
onClick={() => {
changeModalOn(3);
}}
>
<img src={require("../mobile_assets/pipOption.svg")}/>
<div>PIP 모드</div>
<div>{curPip}</div>
</div>
<div
className="qualityOption"
onClick={() => {
changeModalOn(4);
}}
>
<img src={require("../mobile_assets/qualityOption.svg")}/>
<div>화질</div>
<div>{curQuality == "Auto" ? "Auto" : curQuality + "p"}</div>
</div>
</div>
<div
className="footer"
onClick={() => {
changeModalOn(-1);
}}
>
<hr/>
<div className="footer__container">
<div className="footer__li">
<img src={require("../mobile_assets/x.svg")}/>
<div>취소</div>
</div>
</div>
</div>
</div>
</div>
);
};
function mapStateToProps(state) {
const { featureFlags, changeSpeed } = state;
return {
featureFlags,
changeSpeed,
};
}
const connectedLecPlayerV2mobile = connect(mapStateToProps, {
getFeatureFlags: featureActions.getFeatureFlags,
changePlayerSpeed: changePlayerSpeed,
})(LecPlayerV2mobile);
export { connectedLecPlayerV2mobile as LecPlayerV2mobile };
@mixin slideUp ($height) {
animation: animatebottom 0.4s;
@keyframes animatebottom {
from {
bottom: $height;
opacity: 0;
}
to {
bottom: 0;
opacity: 1;
}
}
}
@mixin slideDown($height) {
animation: animatetop 0.6s;
@keyframes animatetop {
from {
bottom: 0;
opacity: 1;
}
to {
bottom: $height;
opacity: 0;
}
}
}
.lec-player-mobile {
width : 100%;
z-index:3;
.video-js {
.skipCircleContainer_R{
height:196%;
aspect-ratio: 1/1;
position:absolute;
border-radius: 50%;
top: -48%;
left: 55%;
overflow:hidden;
//display:none;
visibility: hidden;
.skipCircle_R{
position:relative;
width:50%;
height:51%;
top:24.5%;
background: rgba(0, 0, 0, 0.2);
img{
position:relative;
left:56%;
top:40%;
}
.skipTouch_R{
width: 50%;
height: 60%;
background-color: darkturquoise;
position: absolute;
top: 17%;
right:7%;
visibility: visible;
//display:block;
opacity: 0;
}
}
}
.skipCircleContainer_L{
height:196%;
aspect-ratio: 1/1;
position:absolute;
border-radius: 50%;
top: -48%;
right: 55%;
overflow:hidden;
visibility: hidden;
.skipCircle_L{
position:relative;
width:50%;
height:51%;
top:24.5%;
right: -50%;
background: rgba(0,0,0,0.2);
img{
position:relative;
left:38%;
top:40%;
}
.skipTouch_L{
width: 50%;
height: 60%;
background-color: darkturquoise;
position: absolute;
top: 17%;
left:7%;
visibility: visible;
opacity: 0;
}
}
}
.vjs-big-play-button{
background-color: transparent;
.vjs-icon-placeholder:before{
content : url("../mobile_assets/bigplaybtn.svg");
top: 23px;
left: 0px;
}
}
&.vjs-has-started.vjs-paused .vjs-big-play-button {
display: none;
}
.overlayDiv{
position: absolute;
top: 0;
width: 100%;
height: 100%;
visibility: hidden;
z-index: 0;
background-color: rgba(0, 0, 0, 0.5);
}
&.vjs-has-started.vjs-user-active .overlayDiv,
&.vjs-has-started.vjs-paused .overlayDiv{
visibility: visible;
}
.vjs-play-progress:before {
top : -0.4em;
}
.vjs-play-control {
position: absolute;
top : -23vw;
left: 45%;
outline: none !important;
}
.vjs-play-control .vjs-icon-placeholder:before {
content: url("../mobile_assets/playbtn.svg");
}
.vjs-play-control.vjs-playing .vjs-icon-placeholder:before {
content: url("../mobile_assets/stopbtn.svg");
}
&.vjs-layout-small {
.vjs-time-divider,
.vjs-current-time,
.vjs-duration,{
display:block;
}
}
.vjs-seek-button.skip-back.skip-10 {
position: absolute;
top : -23vw;
left: 25%;
}
.vjs-seek-button.skip-back.skip-10 .vjs-icon-placeholder::before{
-webkit-transform: none;
content: url("../mobile_assets/seekbutton_back.svg");
cursor: pointer;
margin-right: 4px;
}
.vjs-seek-button.skip-forward.skip-10 {
position: absolute;
top : -23vw;
//left: 70vw;
right: 25%;
}
.vjs-seek-button.skip-forward.skip-10 .vjs-icon-placeholder::before{
-webkit-transform: none;
content: url("../mobile_assets/seekbutton_front.svg");
cursor: pointer;
}
.vjs-progress-control{
position: absolute;
bottom: 0px;
height: 2px;
display: flex;
z-index: 1;
}
.vjs-control-bar {
background-color: rgba(0, 0, 0, 0);
display: none;
z-index: 1;
}
&.vjs-has-started .vjs-control-bar {
display: flex;
}
.vjs-volume-panel{
width: 3em;
}
.vjs-volume-bar.vjs-slider-bar,
.vjs-volume-level {
display:none;
}
.vjs-text-track-display {
visibility: hidden;
}
&.vjs-has-started .vjs-text-track-display {
visibility: visible;
}
.vjs-text-track-display > div > div > div{
display: inline-block !important;
font-size: 12px;
font-weight: 600;
padding: 2px 4px;
border-radius: 4px;
background-color: rgba(0, 0, 0, 0.8) !important;
line-height: 1.5;
font-family: Pretendard !important;
}
.vjs-picture-in-picture-control{
display: none;
}
&.vjs-fullscreen {
.vjs-text-track-display {
bottom: 6em;
> div > div > div{
font-size: 15px;
}
}
.vjs-play-control {
left: 48%;
}
.settingbtn {
top: -40vw;
left: 95vw;
}
.vjs-control-bar {
padding-left: 40px;
padding-right: 40px;
}
.vjs-current-time,
.vjs-duration,
.vjs-volume-panel,
.vjs-fullscreen-control,{
bottom: 50px;
}
.vjs-time-divider,
.capsbtn{
position:relative;
bottom: 50px;
}
.vjs-progress-control {
bottom: 40px;
padding-left: 48px;
padding-right: 48px;
height: 3px;
}
&.vjs-has-started.vjs-user-inactive.vjs-playing {
.vjs-progress-control {
visibility: hidden;
opacity: 0;
transition: visibility 0.1s, opacity 0.1s;
}
.vjs-text-track-display{
bottom: 3em;
}
}
}
}
.settingbtn {
position:absolute;
top: -44vw;
left: 93vw;
}
.modalSetting {
position: absolute;
width: 100%;
height: 55vh;
background-color: white;
color: black;
z-index: 2;
bottom: 0;
opacity: 0;
pointer-events: none;
&.open{
@include slideUp(-55vh);
opacity: 1;
pointer-events: auto;
}
&.closed{
opacity: 0;
@include slideDown(-55vh);
}
&__options {
@extend .modalSetting;
animation: none;
z-index: 3;
pointer-events: auto;
opacity: 1;
bottom: 0;
}
.header {
margin: 17px 0px 14px 16px;
font-size: 16px;
font-weight: 600;
line-height: 1.44;
letter-spacing: 0.16px;
}
.menu-bar {
margin-left: 16px;
font-size: 14px;
padding: 10px 0px;
display: flex;
flex-direction: column;
//height: 190px;
justify-content: space-between;
gap: 30px;
> div {
display: flex;
flex-direction: row;
justify-content: left;
div {
margin-left: 18px;
}
div:nth-child(3) {
color: #8B8B8B
}
}
}
.footer {
width: 100%;
position: absolute;
bottom: 0px;
height: 51px;
hr {
width: 100%;
margin: 0px;
}
&__container {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
.footer__li {
margin-left: 16px;
font-size: 14px;
display: flex;
flex-direction: row;
justify-content: left;
div {
margin-left: 18px;
}
}
}
}
}
.fullscreenModal{
@extend .modalSetting;
height: 80vh;
&.open{
//opacity: 1;
pointer-events: auto;
@include slideUp(-80vh);
}
&.closed{
//opacity: 0;
//@include slideDown(-80vh);
animation: none;
}
.header{
margin: 22px 0px 26px 62px;
display: flex;
}
.exitBtn{
position:absolute;
top: 22px;
right: 39px;
}
.menu-bar{
margin-left: 62px;
}
&.modalSetting__options{
height: 80vh;
visibility: hidden;
animation: none;
&.open{
visibility: visible;
}
.menu-bar{
gap: 18px;
}
}
}
}