UNPKG

shaka-player

Version:
684 lines (556 loc) 17.3 kB
/** @license * Shaka Player * Copyright 2016 Google LLC * SPDX-License-Identifier: Apache-2.0 */ /* All of the top-level containers into which various visible features go. */ /* A container for the entire video + controls combo. This is the auto-setup * div which we populate. */ .shaka-video-container { .overlay-parent(); /* Without this, the container somehow winds up being a tad taller than it * should be (484px vs 480px). */ display: flex; /* Set a special font for material design icons. */ .shaka-ui-icon { font-size: var(--shaka-icon-size-sm); } /* Set the fonts for all other content. */ font-family: var(--shaka-font-family); font-weight: normal; -webkit-font-smoothing: antialiased; /* prevent text caret from showing when 'Navigate pages with a text cursor' * setting is enabled on chromium-based browsers */ user-select: none; *, :after, :before { box-sizing: revert; } /* stylelint-disable max-line-length, declaration-block-no-duplicate-properties, unit-no-unknown */ &.pip-mode { background-color: var(--shaka-bg-solid); // Old browsers width: 100vw; height: 100vh; // Modern browsers width: 100dvw; height: 100dvh; } /* stylelint-enable max-line-length, declaration-block-no-duplicate-properties, unit-no-unknown */ } /* Each browser has a different prefixed pseudo-class for fullscreened elements. * Define the properties of a fullscreened element in a mixin, then apply to * each of the browser-specific pseudo-classes. * NOTE: These fullscreen pseudo-classes can't be combined with commas into a * single delcaration. Browsers ignore the rest of the list once they hit one * pseudo-class they don't support. */ .fullscreen-container() { .fill-container(); background-color: var(--shaka-bg-solid); .shaka-text-container, .shaka-speech-to-text-container { /* In fullscreen mode, the text displayer's font size should be relative to * the either window height or width (whichever is smaller), instead of a * fixed size. */ font-size: 4.4vmin; } } .shaka-video-container:fullscreen { .fullscreen-container(); } /* The actual video element. Sits inside .shaka-video-container and gives it a * size in non-fullscreen mode. In fullscreen mode, the sizing relationship * flips. CSS is just great like that. :-( */ .shaka-video { /* At the moment, nothing special is required here. * Note that this should NOT be an overlay-child, as its size could dictate * the size of the container for some applications. */ } /* A container for all controls, including the giant buttons, seek bar, etc. * Sits inside .shaka-video-container, on top of (Z axis) .shaka-video, and * below (Y axis) .shaka-big-buttons-container. */ .shaka-controls-container { .overlay-child(); .hide-when-shaka-controls-disabled(); /* Without this, the controls container overflows the video container. */ box-sizing: border-box; /* A flex container, to make layout of children easier to reason about. */ display: flex; /* Defines in which direction the children should flow. */ flex-direction: column; /* Pushes the children toward the bottom of the container. */ justify-content: flex-end; /* Centers children horizontally. */ align-items: center; /* By default, do not allow any of our controls to shrink. * Specific controls can use .shrinkable() to override. */ * { .unshrinkable(); } /* Position the controls container in front of the text container, so that * the text container doesn't interfere with the control buttons. */ z-index: 1; &[casting="true"] { /* Hide fullscreen button while casting. */ .shaka-fullscreen-button { .hidden(); } } } /* A container for all canvas for LCEVC decoding * Sits inside .shaka-video-container, on top of (Z axis) .shaka-video, and * below (Y axis) .shaka-big-buttons-container. */ .shaka-canvas-container { .overlay-child(); /* Make sure pointer events can reach the video element in the case of native * controls. */ pointer-events: none; } /* A container for VR * Sits inside .shaka-video-container, on top of (Z axis) .shaka-video, and * below (Y axis) .shaka-big-buttons-container. */ .shaka-vr-canvas-container { .overlay-child(); /* Make sure pointer events can reach the video element in the case of native * controls. */ pointer-events: none; } /* Container for controls positioned at the top of the video container: * controls button panel. */ .shaka-top-controls { width: var(--shaka-controls-w); padding: 0; position: absolute; top: 0; /* Position the bottom panel in front of other controls (play button and * spinner containers). * TODO: A different layout arrangement might be a better solution for this. * Need to experiment. */ z-index: 1; } /* Container for controls positioned at the bottom of the video container: * controls button panel and the seek bar. */ .shaka-bottom-controls { width: var(--shaka-controls-w); padding: 0; /* Position the bottom panel in front of other controls (play button and * spinner containers). * TODO: A different layout arrangement might be a better solution for this. * Need to experiment. */ z-index: 1; } .shaka-context-menu { button { .shaka-current-selection-span { display: none; } } } /* The container for the giant buttons. Sits above (Y axis) the * other video controls and seek bar, in the middle of the video frame, on top * of (Z axis) the video. */ .shaka-big-buttons-container { /* Take up as much space as possible, but shrink (vertically) to accomodate * the controls at the bottom. */ margin: 0; .fill-container(); .shrinkable(); .absolute-position(); /* Keep the play button in the middle of this container. */ .center-children(); gap: 2.5%; z-index: 2; pointer-events: none; @big-button-size-percentage: 12.5%; .shaka-ui-icon { width: 75%; height: 75%; } button { width: @big-button-size-percentage; aspect-ratio: 1 / 1; border-radius: 50%; border: none; margin: 0; padding: 0; pointer-events: initial; .center-children(); color: var(--shaka-font-color); background-color: var(--shaka-bg); box-shadow: 0 8px 24px rgba(0, 0, 0, 35%); cursor: pointer; transition: transform 0.18s ease-out, background-color 0.18s ease-out; &:not([disabled]):hover { transform: scale(1.06); background-color: var(--shaka-bg-hover); } &:not([disabled]):active { transform: scale(0.96); } @media (prefers-reduced-motion: reduce) { transition: none; } .show-when-controls-shown(); &[disabled] { cursor: not-allowed; .shaka-controls-container[shown="true"] &, .shaka-controls-container[casting="true"] & { opacity: 0.5; } } } } .shaka-video-container.pip-placeholder { .center-children(); flex-direction: column; gap: 14px; margin: auto; // Clip the scaled poster so it never bleeds outside the player bounds. overflow: hidden; // Poster: contain, sharp, fills the placeholder at the bottom of the stack. .pip-poster { position: absolute; inset: 0; background-size: contain; background-repeat: no-repeat; background-position: center; } // Blur overlay: sits above the poster, blurs everything behind it and // darkens slightly so the icon and label remain legible. .pip-blur-overlay { position: absolute; inset: 0; backdrop-filter: blur(18px) brightness(0.55); z-index: 1; } // Stacking context for everything above the poster. .pip-icon-group { position: relative; .center-children(); z-index: 2; // Size the group to the same footprint as the button so the ring // is centered without extra markup. width: 15%; aspect-ratio: 1 / 1; } // Expanding pulse ring behind the button. .pip-pulse-ring { position: absolute; inset: 0; border-radius: 50%; border: 2px solid rgba(255, 255, 255, 35%); animation: pip-pulse 2s ease-out infinite; pointer-events: none; @media (prefers-reduced-motion: reduce) { animation: none; } } @keyframes pip-pulse { 0% { transform: scale(1); opacity: 0.7; } 100% { transform: scale(1.9); opacity: 0; } } .shaka-ui-icon { width: 75%; height: 75%; } .pip-icon-wrapper { width: 100%; aspect-ratio: 1 / 1; border-radius: 50%; position: relative; .center-children(); color: var(--shaka-font-color); background-color: rgba(0, 0, 0, 45%); border: 1.5px solid rgba(255, 255, 255, 25%); backdrop-filter: blur(6px); cursor: pointer; transition: transform 0.18s ease-out, background-color 0.18s ease-out, border-color 0.18s ease-out; &:hover { transform: scale(1.06); background-color: rgba(0, 0, 0, 62%); border-color: rgba(255, 255, 255, 45%); } &:active { transform: scale(0.96); } @media (prefers-reduced-motion: reduce) { transition: none; } } // Descriptive label below the button. .pip-label { position: relative; z-index: 2; margin: 0; font-size: var(--shaka-title-size); color: rgba(255, 255, 255, 55%); letter-spacing: 0.04em; text-align: center; pointer-events: none; // Subtle text shadow so it reads on any poster colour. text-shadow: 0 1px 6px rgba(0, 0, 0, 80%); } } /* Base appearance shared by every direct control in the button panel. * Applied to direct panel children via `& > *` below, and re-applied to the * mute button nested inside .shaka-mute-volume-container, which intercepts * `& > *` before it can reach the button. */ .panel-button() { /* White text or button icons. */ color: var(--shaka-font-color); height: var(--shaka-icon-size); width: var(--shaka-icon-size); /* Consistent alignment of buttons. */ line-height: 0.5; padding: 0 2px; /* Transparent backgrounds, no borders, and a pointer when you mouse over * them. */ background: transparent; border: 0; cursor: pointer; opacity: 0.9; transition: opacity cubic-bezier(0.4, 0, 0.6, 1) 100ms; text-shadow: 0 0 2px var(--shaka-bg); &:hover { opacity: 1; } } /* This is the container for the horizontal row of controls above the seek bar. * It sits above (Y axis) the seek bar, and below (Y axis) the giant buttons * in the middle. */ .shaka-controls-button-panel, .shaka-controls-top-button-panel { /* Fill the space horizontally, with no extra padding or margin. */ padding: 0; margin: 0; /* This is itself a flex container, with children layed out horizontally. */ display: flex; flex-direction: row; /* Push children to the right. */ justify-content: flex-end; /* Center children vertically. */ align-items: center; /* TODO: Document why. */ overflow: hidden; min-width: 48px; /* Make sure we don't inherit odd font sizes and styles from the environment. * TODO: When did this happen? What forced us to do this? */ font-size: var(--shaka-font-size-sm); font-weight: normal; font-style: normal; /* Make sure contents cannot be selected. */ .unselectable(); .show-when-controls-shown(); /* All buttons, divs, and other controls directly inside this panel should * have these characteristics by default. */ & > * { .panel-button(); /* Use a bit larger icons for some buttons */ &.shaka-fast-forward-button, &.shaka-rewind-button, &.shaka-play-button, &.shaka-skip-previous-button, &.shaka-skip-next-button { .shaka-ui-icon { font-size: var(--shaka-icon-size-lg); } } &.shaka-fullscreen-button, &.shaka-overflow-menu-button { position: relative; .shaka-ui-icon { font-size: var(--shaka-icon-size-sm); } } } /* .shaka-mute-volume-container is a layout wrapper, not a button. * Only non-inherited properties need to be reset here; the button styles * that `& > *` would have given the mute button (which the wrapper * intercepts) are re-applied to the button child via .panel-button(). */ & > .shaka-mute-volume-container { width: auto; height: auto; padding: 0; background: transparent; border: 0; .shaka-mute-button { .panel-button(); } } } /* Buttons hide certain items if they are found inside the control panel */ .shaka-controls-button-panel .shaka-overflow-menu-only, .shaka-controls-top-button-panel .shaka-overflow-menu-only, .shaka-big-buttons-container .shaka-overflow-menu-only { display: none; } .shaka-statistics-container, .shaka-ad-statistics-container { overflow-x: hidden; overflow-y: auto; scrollbar-color: rgba(255, 255, 255, 15%) transparent; scrollbar-width: thin; color: var(--shaka-font-color); background-color: rgba(12, 14, 18, 88%); border: 1px solid rgba(255, 255, 255, 8%); box-shadow: 0 4px 24px rgba(0, 0, 0, 50%); font-size: var(--shaka-font-size); font-family: monospace; padding: 0; border-radius: 10px; position: absolute; z-index: 2; top: 15px; max-height: calc(100% - 115px); .show-when-controls-shown(); /* Header bar (close button row) */ .shaka-statistics-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px 8px; border-bottom: 1px solid rgba(255, 255, 255, 7%); svg { width: 16px; height: 16px; opacity: 0.35; cursor: pointer; transition: opacity 0.15s; &:hover { opacity: 1; } } } /* Section group labels */ .shaka-statistics-section-label { display: block; padding: 8px 14px 3px; font-size: 9px; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; color: rgba(255, 255, 255, 70%); } /* Dividers between groups */ .shaka-statistics-divider { display: block; height: 1px; background: rgba(255, 255, 255, 6%); margin: 6px 14px; padding: 0; } /* Stat rows */ div { display: flex; justify-content: space-between; align-items: center; padding: 3px 14px; gap: 16px; transition: background 0.1s; &:hover { background: rgba(255, 255, 255, 3%); } } /* Row labels */ label { font-size: 11px; color: rgba(255, 255, 255, 45%); white-space: nowrap; } /* Row values */ span { font-size: 11px; color: rgba(255, 255, 255, 90%); font-variant-numeric: tabular-nums; text-align: right; } } .shaka-statistics-container { min-width: 280px; left: 15px; } .shaka-ad-statistics-container { min-width: 220px; right: 15px; } .shaka-scrim-container { margin: 0; width: 100%; position: absolute; left: 0; right: 0; bottom: 0; .shrinkable(); .show-when-controls-shown(); height: 61px; /* A black gradient at the bottom, behind the controls, but only so high. */ background: linear-gradient(rgba(0, 0, 0, 0%) 0, rgba(0, 0, 0, 50%) 100%); } .shaka-text-container, .shaka-speech-to-text-container { .absolute-position(); /* Make sure the text container doesn't steal pointer events from another * layer, such as the ad container. There is nothing interactive in this * layer, so this should be fine. */ pointer-events: none; /* Place the text container on the bottom of the video container. */ bottom: 0%; width: 100%; min-width: 48px; /* When the controls fade in or out, it takes 600ms. Thus, when the text * container adjusts to the presence or absence of controls, we should wait * briefly, so the captions don't end up appearing behind the controls. * Instead of being a gradual animation, this is a fast animation with a * significant delay, since the captions moving around is a little * distracting. */ transition: bottom cubic-bezier(0.4, 0, 0.6, 1) 100ms; transition-delay: 500ms; /* These are defaults which are overridden by JS or cue styles. */ font-size: 20px; line-height: 1.4; // relative to font size. color: var(--shaka-font-color); span.shaka-text-wrapper { display: inline; background: none; } } .shaka-controls-container[shown="true"] ~ .shaka-text-container, .shaka-controls-container[shown="true"] ~ .shaka-speech-to-text-container { /* Disable the transition delay when moving the captions up, so that the * controls don't appear over the captions. */ transition-delay: 0ms; } /* The buffering spinner. */ .shaka-spinner-container { .absolute-position(); .fill-container(); .hide-when-shaka-controls-disabled(); flex-shrink: 1; display: flex; justify-content: center; align-items: center; } .shaka-hidden-fast-forward-container, .shaka-hidden-rewind-container { height: 100%; width: 40%; .shrinkable(); z-index: 1; } .shaka-hidden-fast-forward-container { /* Keep the fast forward button to the right of this container. */ .absolute-position(); left: 60%; } .shaka-hidden-rewind-container { /* keep the rewind button to the left */ .absolute-position(); } .shaka-video-container.no-cursor { cursor: none !important; * { cursor: none !important; } }