ascroll for item/edits

This commit is contained in:
j 2021-10-10 16:06:43 +01:00
parent bbae18bcf3
commit 21db49fe48
19 changed files with 348 additions and 75 deletions

2
.gitignore vendored
View file

@ -2,3 +2,5 @@ venv
__pycache__ __pycache__
db.sqlite3 db.sqlite3
www/ www/
*.swp
venv

View file

@ -2,8 +2,10 @@
.films { .films {
width: 100vw; width: 100vw;
margin: 8px;
.film { .film {
width: 100vw; width: 100vw;
margin-bottom: 16px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
.left, .right { .left, .right {
@ -34,3 +36,15 @@
} }
} }
.film {
.info {
margin: 8px;
}
.play {
text-align: center;
margin: 8px;
margin-top: 16px;
margin-bottom: 16px;
}
}

View file

@ -0,0 +1,8 @@
.texts {
.text {
margin: 8px;
}
}

View file

@ -2,4 +2,5 @@
@import "partials/layout"; @import "partials/layout";
@import "partials/burger"; @import "partials/burger";
@import "partials/film"; @import "partials/film";
@import "partials/text";
@import "partials/ascroll"; @import "partials/ascroll";

View file

@ -31,12 +31,15 @@ function updatePlayerPosition(video, lastKnownScrollPosition) {
video.style.display = 'block'; video.style.display = 'block';
} }
function updatePlayer(video, frame, currentTime) { function updatePlayer(video, frame, currentTime, src) {
var rect = frame.getBoundingClientRect(); var rect = frame.getBoundingClientRect();
video.style.opacity = 0 video.style.opacity = 0
console.log('update player', rect) console.log('update player', rect)
video.style.top = (rect.top + window.scrollY) + 'px' video.style.top = (rect.top + window.scrollY) + 'px'
video.style.display = 'block'; video.style.display = 'block';
if (src) {
video.src = src
}
//video.poster = frame.querySelector('img').src //video.poster = frame.querySelector('img').src
video.currentTime = currentTime video.currentTime = currentTime
video.controls = true video.controls = true
@ -67,55 +70,132 @@ function onVisibilityChange(el, callback) {
} }
} }
function loadItem(config) {
pandoraAPI('get', {id: config.item, keys: ['id', 'title', 'layers']}).then(response => {
var ascroll = document.querySelector('#ascroll')
var loaded = false
pandoraAPI('get', {id: film.id, keys: ['id', 'title', 'layers']}).then(response => { var video = document.createElement('video')
var ascroll = document.querySelector('#ascroll') video.classList.add('player')
var loaded = false video.muted = true
video.src = `${baseURL}/${config.item}/480p.webm`
ascroll.appendChild(video)
var video = document.createElement('video') var h1 = document.createElement('h1')
video.classList.add('player') h1.innerHTML = response.data.title
video.muted = true ascroll.appendChild(h1)
video.src = `${baseURL}/${film.id}/480p.webm`
ascroll.appendChild(video)
var h1 = document.createElement('h1') response.data.layers[layer].forEach(annotation => {
h1.innerHTML = response.data.title if (config.user && annotation.user != config.user) {
ascroll.appendChild(h1) return
}
var div = document.createElement('div')
div.classList.add('annotation')
div.innerHTML = `
<div class="frame"><img src="${baseURL}/${annotation.id.split('/')[0]}/${imageResolution}p${annotation.in}.jpg"></div>
<div class="text">${annotation.value}</div>
response.data.layers[layer].forEach(annotation => { `
var div = document.createElement('div') ascroll.appendChild(div)
div.classList.add('annotation') var frame = div.querySelector('.frame')
div.innerHTML = ` document.addEventListener('scroll', onVisibilityChange(div.querySelector('.frame'), function(visible) {
<div class="frame"><img src="${baseURL}/${annotation.id.split('/')[0]}/${imageResolution}p${annotation.in}.jpg"></div> if (loaded && visible)
<div class="text">${annotation.value}</div> updatePlayer(video, frame, annotation['in'])
` }))
ascroll.appendChild(div) })
var frame = div.querySelector('.frame') loaded = true
document.addEventListener('scroll', onVisibilityChange(div.querySelector('.frame'), function(visible) { let frame = ascroll.querySelector('.annotation .frame')
if (loaded && visible) if (frame) {
updatePlayer(video, frame, annotation['in']) updatePlayer(video, frame, 0)
}
})) /*
document.addEventListener('scroll', function(e) {
lastKnownScrollPosition = window.scrollY;
if (!ticking) {
window.requestAnimationFrame(function() {
updatePlayerPosition(video, lastKnownScrollPosition);
ticking = false;
});
ticking = true;
}
})
*/
}) })
loaded = true }
let frame = ascroll.querySelector('.annotation .frame') function loadEdit(config) {
if (frame) { pandoraAPI('getEdit', {id: config.edit, keys: []}).then(response => {
updatePlayer(video, frame, 0) console.log(response)
} var ascroll = document.querySelector('#ascroll')
var loaded = false
/* var video = document.createElement('video')
document.addEventListener('scroll', function(e) { video.classList.add('player')
lastKnownScrollPosition = window.scrollY; video.muted = true
/*
video.src = `${baseURL}/${config.item}/480p.webm`
*/
ascroll.appendChild(video)
if (!ticking) { var h1 = document.createElement('h1')
window.requestAnimationFrame(function() { var first
updatePlayerPosition(video, lastKnownScrollPosition); h1.innerHTML = response.data.name
ticking = false; ascroll.appendChild(h1)
});
ticking = true; response.data.clips.forEach(clip => {
} clip.layers[layer].forEach(annotation => {
if (config.user && annotation.user != config.user) {
return
}
if (!first) {
first = annotation
}
var div = document.createElement('div')
div.classList.add('annotation')
div.innerHTML = `
<div class="frame"><img src="${baseURL}/${annotation.id.split('/')[0]}/${imageResolution}p${annotation.in}.jpg"></div>
<div class="text">${annotation.value}</div>
`
ascroll.appendChild(div)
var frame = div.querySelector('.frame')
document.addEventListener('scroll', onVisibilityChange(div.querySelector('.frame'), function(visible) {
var src = `${baseURL}/${annotation.id.split('/')[0]}/480p.webm`
if (loaded && visible)
updatePlayer(video, frame, annotation['in'], src)
}))
})
})
loaded = true
let frame = ascroll.querySelector('.annotation .frame')
if (frame) {
var src = `${baseURL}/${ifrst.id.split('/')[0]}/480p.webm`
updatePlayer(video, frame, first['in'], src)
}
/*
document.addEventListener('scroll', function(e) {
lastKnownScrollPosition = window.scrollY;
if (!ticking) {
window.requestAnimationFrame(function() {
updatePlayerPosition(video, lastKnownScrollPosition);
ticking = false;
});
ticking = true;
}
})
*/
}) })
*/ }
})
if (config.item) {
loadItem(config)
} else if (config.edit) {
loadEdit(config)
}

View file

@ -13,16 +13,14 @@
</head> </head>
<body> <body>
<div class="topnav"> <div class="topnav">
<a href="/" class="active"> <a href="/polis" class="active">
phtantasmapolis: Looking Back to the Future phtantasmapolis: Looking Back to the Future
<br> <br>
未竟之城:回顧未來 未竟之城:回顧未來
</a> </a>
<nav> <nav>
<a href="{% url 'films'%}">films - 影片</a> <a href="{% url 'films'%}">films - 影片</a>
<a href="{% url 'edits' %}">edits - 編輯</a> <a href="{% url 'texts' %}">assemblies - </a>
<a href="{% url 'tv' %}">tv - 頻道</a>
<a href="{% url 'essays' %}">essays - 專文</a>
<a href="{% url 'about' %}">about - 關於</a> <a href="{% url 'about' %}">about - 關於</a>
</nav> </nav>
<a class="icon"> <a class="icon">

View file

@ -1,4 +0,0 @@
{% extends "base.html" %}
{% block main %}
some overview page with stuff
{% endblock %}

View file

@ -0,0 +1,90 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
@font-face {
font-family: "wrong font";
src: url("https://files.pad.ma/ttf/wrongfont.woff") format("woff");
}
h1 {
font-family: "wrong font";
margin: auto;
font-weight: normal;
color: yellow;
font-size: 66px;
letter-spacing: 3px;
font-weight: bold;
}
h2 {
color: #6666ff;
font-family: "wrong font";
}
#box {
width: 100%;
height: 100vh;
display: flex;
text-align: center;
}
body {
font-family: "wrong font";
overflow: hidden;
background: black;
}
#shadowBox {
background-color: rgb(0, 0, 0);
/* Fallback color */
background-color: rgba(0, 0, 0, 0.2);
/* Black w/opacity/see-through */
border: 3px solid;
margin: auto;
}
.rainbow_text_animated {
background: linear-gradient(to right, #6666ff, #0099ff , #00ff00, #ff3399, #6666ff);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: rainbow_animation 6s ease-in-out infinite;
background-size: 400% 100%;
}
.reflection {
transform: scaley(-1);
opacity: 0.3;
margin-top: -8px;
}
@keyframes rainbow_animation {
0%,100% {
background-position: 0 0;
}
50% {
background-position: 100% 0;
}
}
</style>
<title>phantas.ma</title>
</head>
<body>
<div id="box">
<div id="shadowBox">
<h1 class="rainbow rainbow_text_animated">Phantas.ma</h1>
<h2></h2>
</div>
</div>
<script>
path = document.location.pathname.slice(1)
if (path) {
document.querySelector('h1').innerHTML = 'Phantas.ma/' + path
if (path == 'polis') {
setTimeout(() => {
document.querySelector('h2').innerHTML = 'Oct 30, 2021 Mar 6, 2022'
})
}
}
</script>

View file

@ -1,21 +1,23 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block main %} {% block main %}
<video src="{{ settings.TIMELINE_PREFIX }}{{ film.padma_id }}/loop.mp4" autoplay loop muted></video> <div class="film">
<div class="info">
<h1>{{ film.data.title | safe }}</h1> <h1>{{ film.data.title | safe }}</h1>
<h2>{{ film.data.director|join:", "|safe }}</h2> <h2>{{ film.data.director|join:", "|safe }}</h2>
<p>{{ film.data.summary|safe }}</p> <p>{{ film.data.summary|safe }}</p>
</div>
<div> <video src="{{ settings.TIMELINE_PREFIX }}{{ film.padma_id }}/loop.mp4" autoplay loop muted></video>
<a href="" id="play-fullscreen">Watch Fullscreen</a> <div class="play">
</div> <div>
<div> <a href="" id="play-fullscreen">Watch Fullscreen</a>
<a href="play/en">Watch with Annotations</a> </div>
</div> {% for text in film.related_texts %}
<div> <div>
<a href="play/zh">Watch with Annotations (Mandarin title)</a> <a href="{{ text.get_absolute_url }}">{{ text.title }}</a>
</div>
{% endfor %}
</div> </div>
{% endblock %} {% endblock %}

View file

@ -7,11 +7,14 @@
<h1><a href="{% url 'film' film.slug %}">{{ film.data.title | safe }}</a></h1> <h1><a href="{% url 'film' film.slug %}">{{ film.data.title | safe }}</a></h1>
<h2>{{ film.data.director|join:", "|safe }}</h2> <h2>{{ film.data.director|join:", "|safe }}</h2>
</div> </div>
{% comment %}
<div class="right"> <div class="right">
<a href="{% url 'film' film.slug %}"> <a href="{% url 'film' film.slug %}">
<img src="{{ settings.TIMELINE_PREFIX }}{{ film.padma_id }}/timeline.jpg"> <img src="{{ settings.TIMELINE_PREFIX }}{{ film.padma_id }}/timeline.jpg">
</a> </a>
</div> </div>
{% endcomment %}
</div> </div>
{% endfor %} {% endfor %}
</div> </div>

View file

@ -1,4 +1,11 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block main %} {% block main %}
some overview page with stuff <div>
<div>
Phantas.ma/polis is mainly in support by the Cultural Taiwan Foundation and in cooperation with SEA plateaus
<div>
</div>
本計畫由財團法人文化臺灣基金會支持、SEA plateaus協力
</div>
</div>
{% endblock %} {% endblock %}

10
app/templates/text.html Normal file
View file

@ -0,0 +1,10 @@
{% extends "base.html" %}
{% block main %}
<div id="ascroll"></div>
{% endblock %}
{% block end %}
<script>
var config = {{ text.json | safe }};
</script>
<script src="/static/js/ascroll.js"></script>
{% endblock %}

10
app/templates/texts.html Normal file
View file

@ -0,0 +1,10 @@
{% extends "base.html" %}
{% block main %}
<div class="texts">
{% for text in texts %}
<div class="text">
<a href="{{ text.get_absolute_url }}">{{ text.title }}</a>
</div>
{% endfor %}
{% endblock %}

View file

@ -1,3 +1,12 @@
from django.contrib import admin from django.contrib import admin
# Register your models here. from . import models
@admin.decorators.register(models.Text)
class TextAdmin(admin.ModelAdmin):
pass
@admin.decorators.register(models.Page)
class PageAdmin(admin.ModelAdmin):
pass

View file

@ -0,0 +1,17 @@
# Generated by Django 3.2.7 on 2021-10-10 14:25
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('text', '0001_initial'),
]
operations = [
migrations.RenameModel(
old_name='Essay',
new_name='Text',
),
]

View file

@ -1,5 +1,7 @@
import logging import logging
import json
from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db import models from django.db import models
@ -19,15 +21,27 @@ class Page(models.Model):
teaser = models.TextField() teaser = models.TextField()
body = models.TextField() body = models.TextField()
class Essay(models.Model): class Text(models.Model):
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True) modified = models.DateTimeField(auto_now=True)
slug = models.SlugField() slug = models.SlugField()
public = models.BooleanField(default=False) public = models.BooleanField(default=False)
title = models.TextField()
teaser = models.TextField(default="", blank=True)
body = models.TextField(default="", blank=True)
data = models.JSONField(default=dict) data = models.JSONField(default=dict)
title = models.TextField() def __str__(self):
teaser = models.TextField() return self.title
body = models.TextField()
def get_absolute_url(self):
return '/' + settings.URL_PREFIX + 'assemblies/' + self.slug
def json(self):
data = {}
data['title'] = self.title
data.update(self.data)
return json.dumps(data)

View file

@ -2,6 +2,10 @@ from django.shortcuts import render, redirect, get_object_or_404
from . import models from . import models
def fallback(request):
context = {}
return render(request, 'fallback.html', context)
def index(request): def index(request):
context = {} context = {}
return render(request, 'index.html', context) return render(request, 'index.html', context)
@ -10,12 +14,12 @@ def about(request):
context = {} context = {}
return render(request, 'about.html', context) return render(request, 'about.html', context)
def essays(request): def texts(request):
context = {} context = {}
context['essays'] = models.Essay.objects.filter(public=True).order_by('created') context['texts'] = models.Text.objects.filter(public=True).order_by('created')
return render(request, 'essays.html', context) return render(request, 'texts.html', context)
def essay(request, slug, lang): def text(request, slug):
context = {} context = {}
context['essay'] = get_object_or_404(models.Essay, slug=slug) context['text'] = get_object_or_404(models.Text, slug=slug)
return render(request, 'essay.html', context) return render(request, 'text.html', context)

View file

@ -30,9 +30,10 @@ urlpatterns = [
path(settings.URL_PREFIX + 'edit/<str:slug>/play/<str:lang>', video.edit_play, name='edit_play'), path(settings.URL_PREFIX + 'edit/<str:slug>/play/<str:lang>', video.edit_play, name='edit_play'),
path(settings.URL_PREFIX + 'edit/<str:slug>/', video.edit, name='edit'), path(settings.URL_PREFIX + 'edit/<str:slug>/', video.edit, name='edit'),
path(settings.URL_PREFIX + 'tv/', video.tv, name='tv'), path(settings.URL_PREFIX + 'tv/', video.tv, name='tv'),
path(settings.URL_PREFIX + 'essays/', text.essays, name='essays'), path(settings.URL_PREFIX + 'assemblies/', text.texts, name='texts'),
path(settings.URL_PREFIX + 'essay/<str:slug>/<str:lang>', text.essay, name='essay'), path(settings.URL_PREFIX + 'assemblies/<str:slug>', text.text, name='text'),
path(settings.URL_PREFIX + 'about/', text.about, name='about'), path(settings.URL_PREFIX + 'about/', text.about, name='about'),
path(settings.URL_PREFIX[:-1], text.index, name='index'), path(settings.URL_PREFIX[:-1], text.index, name='index'),
path('', text.fallback, name='fallback'),
] ]

View file

@ -22,6 +22,13 @@ class Film(models.Model):
def __str__(self): def __str__(self):
return self.data.get('title', self.slug) return self.data.get('title', self.slug)
def get_absolute_url(self):
return '/' + settings.URL_PREFIX + 'film/' + self.slug
def related_texts(self):
from ..text.models import Text
return Text.objects.filter(data__item=self.padma_id)
class Edit(models.Model): class Edit(models.Model):
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)