|
|
|
<template>
|
|
|
|
|
|
|
|
<header>
|
|
|
|
<h1>Story Baobab</h1>
|
|
|
|
<div class="intro">
|
|
|
|
Drag and drop images and video to sketch a print-friendly storyboard. Double click on shots' title & descriptions to edit!
|
|
|
|
</div>
|
|
|
|
</header>
|
|
|
|
|
|
|
|
<main>
|
|
|
|
|
|
|
|
<div class="insert-scene">
|
|
|
|
<input v-model="title" placeholder="Title">
|
|
|
|
<button @click="add">Add Scene</button>
|
|
|
|
</div>
|
|
|
|
<div class="load-scene">
|
|
|
|
<label > Open
|
|
|
|
<input type="file" multiple @input="load">
|
|
|
|
</label>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<SceneEntry
|
|
|
|
v-for="(scene, index) in scenes"
|
|
|
|
:title="scene.title"
|
|
|
|
:series="scene.series"
|
|
|
|
@remove="remove(index)"
|
|
|
|
/>
|
|
|
|
|
|
|
|
</main>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
|
|
import SceneEntry from './components/SceneEntry.vue'
|
|
|
|
import {utils} from './composables/utils.js'
|
|
|
|
import {ref} from 'vue'
|
|
|
|
|
|
|
|
const {base64ToBlob} = utils()
|
|
|
|
|
|
|
|
const scenes = ref([])
|
|
|
|
const title = ref('')
|
|
|
|
const add = () => {
|
|
|
|
scenes.value.push({
|
|
|
|
title: title.value,
|
|
|
|
series: []
|
|
|
|
})
|
|
|
|
title.value = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
const remove = (index) => {
|
|
|
|
scenes.value.splice(index, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
const load = (e) => {
|
|
|
|
let files = e.target.files
|
|
|
|
files.forEach((file) =>{
|
|
|
|
const reader = new FileReader()
|
|
|
|
reader.readAsText(file)
|
|
|
|
reader.onloadend = async (ev) => {
|
|
|
|
if (ev.target.readyState == FileReader.DONE) {
|
|
|
|
try {
|
|
|
|
let scene = JSON.parse(reader.result)
|
|
|
|
|
|
|
|
await Promise.all(scene.series.map(async (shot) =>{
|
|
|
|
let file = await base64ToBlob(shot.img, 'snapshot')
|
|
|
|
let filePath = (window.URL || window.webkitURL).createObjectURL(file)
|
|
|
|
shot.img = filePath
|
|
|
|
shot.file = file
|
|
|
|
}))
|
|
|
|
|
|
|
|
scenes.value.push(scene)
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
console.log(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
|
|
|
header {
|
|
|
|
margin: 32px;
|
|
|
|
}
|
|
|
|
|
|
|
|
header h1 {
|
|
|
|
margin: 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
main {
|
|
|
|
margin: 32px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.insert-scene {
|
|
|
|
display: inline-flex;
|
|
|
|
gap: 8px;
|
|
|
|
padding: 16px;
|
|
|
|
background-color: #F3F3F3;
|
|
|
|
}
|
|
|
|
|
|
|
|
.load-scene {
|
|
|
|
display: inline-flex;
|
|
|
|
gap: 8px;
|
|
|
|
padding: 16px;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
.load-scene input {
|
|
|
|
position: absolute;
|
|
|
|
top: -10000px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.load-scene label {
|
|
|
|
background-color: #fff;
|
|
|
|
border: 1px solid currentColor;
|
|
|
|
padding: 8px;
|
|
|
|
border-radius: 50%;
|
|
|
|
cursor: pointer;
|
|
|
|
}
|
|
|
|
|
|
|
|
.load-scene label:hover {
|
|
|
|
background-color: #393939;
|
|
|
|
border-color: #393939;
|
|
|
|
color: #fff;
|
|
|
|
}
|
|
|
|
|
|
|
|
@media print {
|
|
|
|
|
|
|
|
header, .insert-scene, .load-scene, .add-shot {
|
|
|
|
display: none
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
</style>
|