split utils, fix pandora-scroll element

This commit is contained in:
j 2022-09-03 11:21:16 +02:00
parent a1eec24f17
commit b1cd3e292b
5 changed files with 260 additions and 213 deletions

View file

@ -4,6 +4,9 @@
min-height: calc(100vh - 64px); min-height: calc(100vh - 64px);
} }
} }
#video-overlay {
z-index: 11;
}
#ascroll { #ascroll {
font-family: "noto_sans"; font-family: "noto_sans";
width: 100%; width: 100%;
@ -46,8 +49,18 @@
max-height: 88vh; max-height: 88vh;
margin: auto; margin: auto;
//transition: opacity 0.4s; //transition: opacity 0.4s;
z-index: 10;
} }
.left {
float: left;
padding-right: 1vw;
width: 30%;
}
.clearpara {
clear: left;
padding-top: 1vh;
}
.annotation { .annotation {
min-height: 100vh; min-height: 100vh;
.frame { .frame {

View file

@ -1,35 +1,22 @@
var layer = 'keywords' var layer = 'keywords'
var imageResolution = 480 var imageResolution = 480
var videoExtension
function setVideoSrc(video, src) {
var ext
if (!videoExtension) {
[
['video/mp4; codecs="avc1.42E01E, mp4a.40.2"', '.mp4'],
['video/webm; codecs="vp8, vorbis"', '.webm'],
].forEach(opt => {
if (videoExtension) { return }
if (video.canPlayType(opt[0]).replace('no', '')) {
videoExtension = opt[1]
}
})
}
src = src.replace('.webm', videoExtension)
if (src != video.src) {
video.src = src
}
}
function resize() { function resize() {
var video = document.querySelector('video') var video = document.querySelector('video')
if (video && video._frame) { if (video && video._frame) {
var rect = video._frame.getBoundingClientRect(); var top, rect = video._frame.getBoundingClientRect();
video.style.top = (rect.top + window.scrollY) + 'px' if (video._root) {
var root_rect = video._root.getBoundingClientRect();
top = rect.top - root_rect.top
} else {
top = rect.top + window.scrollY
}
video.style.top = top + 'px'
} }
} }
function updatePlayer(video, frame, currentTime, out, src, config) { function updatePlayer(video, frame, currentTime, out, src, config) {
//console.log('update player', currentTime, out, src)
var top, rect = frame.getBoundingClientRect(); var top, rect = frame.getBoundingClientRect();
video.style.opacity = 0 video.style.opacity = 0
if (config.root) { if (config.root) {
@ -41,6 +28,7 @@ function updatePlayer(video, frame, currentTime, out, src, config) {
} }
video.style.top = top + 'px' video.style.top = top + 'px'
video.style.display = 'block'; video.style.display = 'block';
video.style.maxWidth = rect.width + 'px'
if (src) { if (src) {
setVideoSrc(video, src) setVideoSrc(video, src)
} }
@ -50,6 +38,7 @@ function updatePlayer(video, frame, currentTime, out, src, config) {
video.currentTime = currentTime video.currentTime = currentTime
video.dataset.in = currentTime video.dataset.in = currentTime
video.dataset.out = out video.dataset.out = out
video._root = config.root
video._frame = frame video._frame = frame
const show = event => { const show = event => {
video.style.opacity = 1 video.style.opacity = 1
@ -60,51 +49,6 @@ function updatePlayer(video, frame, currentTime, out, src, config) {
video.play() video.play()
} }
function isElementInViewport (el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /* or $(window).height() */
Math.floor(rect.right) <= (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */
);
}
function onVisibilityChange(el, callback) {
var old_visible;
return function () {
var visible = isElementInViewport(el);
if (visible != old_visible) {
old_visible = visible;
if (visible) {
el.classList.add('visible')
} else {
el.classList.remove('visible')
}
if (typeof callback == 'function') {
callback(visible);
}
}
}
}
function scrollTo(element) {
var delta = element.offsetTop - document.scrollingElement.scrollTop,
duration = 1000, t = 40, n = duration / t,
step = delta / n;
function scroll() {
if (document.scrollingElement.scrollTop + step > element.offsetTop) {
document.scrollingElement.scrollTop = element.offsetTop
n = 0
} else {
document.scrollingElement.scrollTop += step
}
n--
if (n) setTimeout(scroll, t)
}
scroll()
}
function timeupdate(event) { function timeupdate(event) {
if (event.target.dataset.out && event.target.dataset.in) { if (event.target.dataset.out && event.target.dataset.in) {
@ -156,59 +100,7 @@ function formatInfo(config, ascroll) {
return info return info
} }
function showOverlay(event) { function renderAnnotation(config, video, root, clip) {
event.stopPropagation()
event.preventDefault()
document.querySelectorAll('#video-overlay').forEach(element => element.remove())
var video = event.target
var rect = video.getBoundingClientRect();
var overlay = document.createElement('div')
overlay.id = 'video-overlay'
overlay.style.top = video.style.top
overlay.style.width = rect.width + 'px'
overlay.style.height = rect.height + 'px'
overlay.style.position = 'absolute'
overlay.style.display = 'flex'
overlay.style.alignItems = 'center'
overlay.style.justifyContent = 'center'
//overlay.style.fontSize = '45px'
video.controls = false
var off = `<span class="annotation-icon-wrapper animated"><span class="text f-icon-volume_off annotation-icon"></span>`
var on = `<span class="annotation-icon-wrapper"><span class="f-icon-volume_down annotation-icon"></span></span>`
if (video.muted) {
overlay.innerHTML = off
} else {
overlay.innerHTML = on
}
overlay.addEventListener('click', event=> {
const muted = video.muted
document.querySelectorAll('pandora-scroll').forEach(el => el.mute() )
video.muted = !muted
if (video.muted) {
overlay.innerHTML = off
} else {
overlay.innerHTML = on
}
})
var timeout = setTimeout(() => {
video.controls = false
overlay.remove()
}, 3000)
overlay.addEventListener('mousemove', event=> {
clearTimeout(timeout)
timeout = setTimeout(() => {
video.controls = false
overlay.remove()
}, 500)
})
video.parentElement.appendChild(overlay)
}
function renderAnnotation(config, video, ascroll, clip) {
console.log("renderAnnotation", clip)
var div = document.createElement('div') var div = document.createElement('div')
div.classList.add('annotation') div.classList.add('annotation')
var annotation = clip.annotations[0] var annotation = clip.annotations[0]
@ -239,22 +131,23 @@ function renderAnnotation(config, video, ascroll, clip) {
</div> </div>
<div class="text">${values}</div> <div class="text">${values}</div>
` `
ascroll.appendChild(div) root.appendChild(div)
var frame = div.querySelector('.frame') var frame = div.querySelector('.frame')
document.addEventListener('scroll', onVisibilityChange(div.querySelector('.frame'), function(visible) { document.addEventListener('scroll', onVisibilityChange(div.querySelector('.frame'), function(visible) {
var src var src
if (config.edit) { if (clip.src) {
src = clip.src
} else if (config.edit) {
src = `${streamPrefix}/${annotation.id.split('/')[0]}/480p.webm` src = `${streamPrefix}/${annotation.id.split('/')[0]}/480p.webm`
} }
if (config.loaded && visible) { if (config.loaded && visible) {
updatePlayer(video, frame, annotation['in'], annotation['out'], src, config) updatePlayer(video, frame, annotation['in'], annotation['out'], src, config)
} }
})) }))
} }
function renderAnnotations(config) { function renderAnnotations(config) {
console.log('renderAnnotations', config.item, config.annotations.length) //console.log('renderAnnotations', config.item, config.annotations.length)
var ascroll = config.root ? config.root : document.querySelector('#ascroll') var ascroll = config.root ? config.root : document.querySelector('#ascroll')
config.loaded = false config.loaded = false
var video = document.createElement('video') var video = document.createElement('video')
@ -292,22 +185,9 @@ function renderAnnotations(config) {
} }
} }
function isInside(config, annotation) {
if (!config['in'] && !config['out']) {
return true
}
if (annotation['in'] < config['out'] && annotation['out'] > config['in']) {
annotation['in'] = Math.min(annotation['in'], config['in'])
annotation['out'] = Math.min(annotation['out'], config['out'])
return true
}
return false
}
async function loadClips(annotations) { async function loadClips(annotations) {
var items = annotations.map(annotation => annotation.id.split('/')[0]) var items = annotations.map(annotation => annotation.id.split('/')[0])
items = [...new Set(items)] items = [...new Set(items)]
console.log('loadClips', annotations, items)
return pandoraAPI('findClips', {itemsQuery: { return pandoraAPI('findClips', {itemsQuery: {
conditions: [{key: 'id', operator: '&', value: items}] conditions: [{key: 'id', operator: '&', value: items}]
}, range: [0, 10000], keys: [ }, range: [0, 10000], keys: [
@ -348,7 +228,7 @@ async function loadClips(annotations) {
}) })
} }
function loadAnnotations(config) { async function loadAnnotations(config) {
var layers = config.layer || config.layers || [] var layers = config.layer || config.layers || []
if (!Array.isArray(layers)) { if (!Array.isArray(layers)) {
layers = [layers] layers = [layers]
@ -370,9 +250,9 @@ function loadAnnotations(config) {
"color2": "", "color2": "",
} }
] ]
renderAnnotations(config) return config
} else if (config.item) { } else if (config.item) {
pandoraAPI('get', {id: config.item, keys: [ return pandoraAPI('get', {id: config.item, keys: [
'layers' 'layers'
]}).then(response => { ]}).then(response => {
var annotations = [] var annotations = []
@ -386,8 +266,7 @@ function loadAnnotations(config) {
} }
}) })
}) })
loadClips(annotations).then(annotations => { return loadClips(annotations).then(annotations => {
console.log('got', annotations)
config.annotations = annotations.filter(annotation => { config.annotations = annotations.filter(annotation => {
if (config.only_e) { if (config.only_e) {
if (annotation.value.slice(0, 2) == 'E:') { if (annotation.value.slice(0, 2) == 'E:') {
@ -400,13 +279,13 @@ function loadAnnotations(config) {
return annotation return annotation
} }
}) })
renderAnnotations(config) return config
}) })
}) })
} else { } else {
var cited = {} var cited = {}
if (config.edit) { if (config.edit) {
pandoraAPI('getEdit', {id: config.edit, keys: []}).then(response => { return pandoraAPI('getEdit', {id: config.edit, keys: []}).then(response => {
var annotations = [] var annotations = []
response.data.clips.forEach(clip => { response.data.clips.forEach(clip => {
cited[clip.item] = { cited[clip.item] = {
@ -435,17 +314,17 @@ function loadAnnotations(config) {
}) })
}) })
}) })
loadClips(annotations).then(annotations => { return loadClips(annotations).then(annotations => {
config.annotations = annotations config.annotations = annotations
config.cited = Object.values(cited) config.cited = Object.values(cited)
renderAnnotations(config) return config
}) })
}) })
} else { } else {
if (!config.annotations) { if (!config.annotations) {
config.annotations = [] config.annotations = []
} }
renderAnnotations(config) return config
} }
} }
} }
@ -455,7 +334,7 @@ config.layer = config.layer || layer
if (false && config.annotations) { if (false && config.annotations) {
renderAnnotations(config) renderAnnotations(config)
} else { } else {
loadAnnotations(config) loadAnnotations(config).then(renderAnnotations)
} }
window.addEventListener('resize', resize, false); window.addEventListener('resize', resize, false);

View file

@ -20,6 +20,10 @@ class PandoraScroll extends HTMLElement {
shadow.appendChild(link) shadow.appendChild(link)
shadow.appendChild(style) shadow.appendChild(style)
shadow.appendChild(body) shadow.appendChild(body)
window.addEventListener('resize', () => {
this._resize()
}, false)
this.mute = function() { this.mute = function() {
shadow.querySelectorAll('video').forEach(video => { shadow.querySelectorAll('video').forEach(video => {
video.muted = true video.muted = true
@ -31,19 +35,18 @@ class PandoraScroll extends HTMLElement {
static get observedAttributes() { return ['muted']; } static get observedAttributes() { return ['muted']; }
connectedCallback() { connectedCallback() {
console.log('scroll connected')
this._loadAnnotations() this._loadAnnotations()
this._updateStyle() this._updateStyle()
} }
disconnectedCallback() { disconnectedCallback() {
console.log('scroll disconnected') //console.log('scroll disconnected')
} }
adoptedCallback() { adoptedCallback() {
console.log('Custom square element moved to new page.'); //console.log('element moved to new page.');
} }
attributeChangedCallback(name, oldValue, newValue) { attributeChangedCallback(name, oldValue, newValue) {
console.log('value changed', name, oldValue, newValue); //console.log('value changed', name, oldValue, newValue);
this._updateStyle() this._updateStyle()
} }
_updateStyle() { _updateStyle() {
@ -79,29 +82,42 @@ class PandoraScroll extends HTMLElement {
_loadAnnotations() { _loadAnnotations() {
var config = this._config() var config = this._config()
config.root = this.shadowRoot.querySelector('.pandora-scroll') config.root = this.shadowRoot.querySelector('.pandora-scroll')
console.log(config) config.video = document.querySelector('video')
loadAnnotations(config) loadAnnotations(config).then(config => {
config.annotations.forEach(annotation => {
annotation.src = `${streamPrefix}/${annotation.id.split('/')[0]}/480p.webm`
renderAnnotation(window.config, config.video, config.root, annotation)
})
})
}
_resize() {
var video = this.shadowRoot.querySelector('video')
if (video && video._frame) {
var rect = video._frame.getBoundingClientRect(),
root_rect = video._root.getBoundingClientRect(),
top = rect.top - root_rect.top;
video.style.top = top + 'px'
}
}
}
class PandoraItem extends HTMLElement {
constructor() {
super()
}
}
class PandoraEdit extends HTMLElement {
constructor() {
super()
} }
} }
customElements.define("pandora-scroll", PandoraScroll); customElements.define("pandora-scroll", PandoraScroll);
customElements.define("pandora-item", PandoraItem);
customElements.define("pandora-edit", PandoraEdit);
function parseTime(value) {
return value.split(":").map(p => parseFloat(p)).reduce((c, p) => { return c*60 + p}, 0)
}
function isInside(config, annotation) {
if (!config['in'] && !config['out']) {
return true
}
if (annotation['in'] < config['out'] && annotation['out'] > config['in']) {
return true
}
return false
}
function parseIframeURL(value) { function parseIframeURL(value) {
var config = {} var config = {}
value = value.replace('/player/', '/') value = value.replace('/player/', '/')
@ -114,7 +130,7 @@ function parseIframeURL(value) {
} }
var args = data[1].replace('&amp;', '&').split('&').map(kv => { var args = data[1].replace('&amp;', '&').split('&').map(kv => {
kv = kv.split('=') kv = kv.split('=')
console.log(kv, decodeValue(kv[1])) //console.log(kv, decodeValue(kv[1]))
return [kv[0], JSON.parse(decodeValue(kv[1]))] return [kv[0], JSON.parse(decodeValue(kv[1]))]
}).reduce((k, v) => { }).reduce((k, v) => {
k[v[0]] = v[1] k[v[0]] = v[1]

138
app/static/js/utils.js Normal file
View file

@ -0,0 +1,138 @@
var videoExtension
function setVideoSrc(video, src) {
var ext
if (!videoExtension) {
[
['video/mp4; codecs="avc1.42E01E, mp4a.40.2"', '.mp4'],
['video/webm; codecs="vp8, vorbis"', '.webm'],
].forEach(opt => {
if (videoExtension) { return }
if (video.canPlayType(opt[0]).replace('no', '')) {
videoExtension = opt[1]
}
})
}
src = src.replace('.webm', videoExtension)
if (src != video.src) {
video.src = src
}
}
function scrollTo(element) {
var delta = element.offsetTop - document.scrollingElement.scrollTop,
duration = 1000, t = 40, n = duration / t,
step = delta / n;
function scroll() {
if (document.scrollingElement.scrollTop + step > element.offsetTop) {
document.scrollingElement.scrollTop = element.offsetTop
n = 0
} else {
document.scrollingElement.scrollTop += step
}
n--
if (n) setTimeout(scroll, t)
}
scroll()
}
function showOverlay(event) {
console.log('show overlay', event.target, event)
event.stopPropagation()
event.preventDefault()
document.querySelectorAll('#video-overlay').forEach(element => element.remove())
var video = event.target
var rect = video.getBoundingClientRect();
if (video._frame) {
rect = video._frame.getBoundingClientRect();
}
var overlay = document.createElement('div')
overlay.id = 'video-overlay'
overlay.style.top = video.style.top
overlay.style.width = rect.width + 'px'
overlay.style.height = rect.height + 'px'
overlay.style.position = 'absolute'
overlay.style.display = 'flex'
overlay.style.alignItems = 'center'
overlay.style.justifyContent = 'center'
//overlay.style.fontSize = '45px'
video.controls = false
var off = `<span class="annotation-icon-wrapper animated"><span class="text f-icon-volume_off annotation-icon"></span>`
var on = `<span class="annotation-icon-wrapper"><span class="f-icon-volume_down annotation-icon"></span></span>`
if (video.muted) {
overlay.innerHTML = off
} else {
overlay.innerHTML = on
}
overlay.addEventListener('click', event=> {
const muted = video.muted
document.querySelectorAll('pandora-scroll').forEach(el => el.mute() )
video.muted = !muted
if (video.muted) {
overlay.innerHTML = off
} else {
overlay.innerHTML = on
}
})
var timeout = setTimeout(() => {
video.controls = false
overlay.remove()
}, 3000)
overlay.addEventListener('mousemove', event=> {
clearTimeout(timeout)
timeout = setTimeout(() => {
video.controls = false
overlay.remove()
}, 500)
})
video.parentElement.appendChild(overlay)
}
function isInside(config, annotation) {
if (!config['in'] && !config['out']) {
return true
}
if (annotation['in'] < config['out'] && annotation['out'] > config['in']) {
annotation['in'] = Math.min(annotation['in'], config['in'])
annotation['out'] = Math.min(annotation['out'], config['out'])
return true
}
return false
}
function isElementInViewport (el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /* or $(window).height() */
Math.floor(rect.right) <= (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */
);
}
function onVisibilityChange(el, callback) {
var old_visible;
return function () {
var visible = isElementInViewport(el);
if (visible != old_visible) {
old_visible = visible;
if (visible) {
el.classList.add('visible')
} else {
el.classList.remove('visible')
}
if (typeof callback == 'function') {
callback(visible);
}
}
}
}
function parseTime(value) {
return value.split(":").map(p => parseFloat(p)).reduce((c, p) => { return c*60 + p}, 0)
}

View file

@ -33,6 +33,7 @@
{% if text.data.view == 'player' %} {% if text.data.view == 'player' %}
<script src="/static/js/player.js?2021112223"></script> <script src="/static/js/player.js?2021112223"></script>
{% else %} {% else %}
<script src="/static/js/utils.js?2021112223"></script>
<script src="/static/js/pandora-scroll.js?2021112223"></script> <script src="/static/js/pandora-scroll.js?2021112223"></script>
<script src="/static/js/ascroll.js?2021112223"></script> <script src="/static/js/ascroll.js?2021112223"></script>
{% endif %} {% endif %}