basic scene add and remove, video snapshot

main
km0 2 years ago
parent c17ef3c5fa
commit 64e14c715d

@ -2,9 +2,10 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/baobab.svg" />
<link rel="stylesheet" href="/style.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title> <title>Story Baobab</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

@ -0,0 +1,3 @@
<svg width="556" height="512" viewBox="0 0 556 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M96.446 150.839C98.1439 150.942 99.9498 151.965 101.644 151.644C102.984 152.314 102.414 153.731 103.932 154.088C106.995 154.808 110.133 154.525 113.211 155.283C115.824 155.927 118.313 156.847 121.035 156.895C124.044 156.948 127.028 157.162 129.534 159.078C134.263 162.695 137.476 168.446 141.308 172.958C142.985 174.932 145.022 177.393 147.417 178.546C150.227 179.899 154.514 177.242 157.06 176.389C167.503 172.888 178.592 171.636 188.51 166.616C191.163 165.273 195.316 160.677 198.621 161.833C200.545 162.507 202.257 165.537 203.377 166.928C209.492 174.521 215.284 182.368 221.468 189.905C223.507 192.39 224.932 193.322 227.388 194.927L227.498 194.999C232.671 198.381 232.032 206.669 232.541 212.076C233.068 217.684 233.32 223.889 232.385 229.465C231.286 236.015 226.905 241.379 225.783 248.049C221.773 271.887 221.411 295.432 221.858 319.579C222.146 335.152 221.561 420.168 218.817 435.507C217.266 444.175 213.895 453.173 213.696 461.993C213.523 469.719 213.341 481.141 221.364 485.152C227.625 488.283 234.719 489.312 241.43 491.026C248.663 492.874 255.343 495.028 262.795 496.017C281.393 498.486 302.866 501.724 321.07 495.549C332.857 491.551 345.778 484.596 353.092 474.235C357.9 467.424 352.425 458.004 349.609 451.414C342.082 433.797 341.025 343.835 337.081 325.298L337.048 325.146C335.668 318.656 334.065 311.123 330.271 305.518L330.055 305.199C328.91 303.508 327.788 301.853 326.814 300.033C321.083 289.329 316.552 277.446 313.48 265.698C312.737 262.858 313.054 260.96 313.402 258.108C315.022 244.826 322.234 233.932 329.469 223.005L329.907 222.343C335.326 214.155 339.953 204.734 346.594 197.442C348.188 195.692 350.178 194.38 351.688 192.556C353.136 190.807 356.801 189.506 358.81 188.241C361.132 186.779 361.228 184.561 362.813 182.367C364.485 180.052 364.113 177.497 365.048 174.959C366.219 171.782 367.279 167.213 369.207 164.458C369.686 163.774 370.782 163.473 371.442 162.977L371.715 162.773C373.996 161.062 376.18 159.425 378.278 157.467C381.137 154.798 381.027 160.427 383.893 160.612C391.965 161.132 400.669 159.582 408.611 158.402C414.882 157.471 420.575 157.333 426.234 154.296C428.027 153.333 429.528 151.636 431.328 150.865C432.648 150.299 435.81 148.985 437.306 149.253C439.783 149.698 442.271 150.236 444.792 150.631C451.691 151.711 458.179 154.453 465.066 155.491C468.872 156.065 475.059 156.941 478.062 153.802C482.008 149.676 484.772 144.369 488.433 139.948C491.058 136.777 493.947 132.902 494.385 128.615C494.738 125.153 493.701 121.511 493.501 118.063C493.107 111.263 495.204 105.936 496.958 99.5043C498.233 94.8281 497.657 89.9598 496.698 85.2866C495.147 77.7269 490.436 72.2444 485.314 66.7282C480.39 61.4256 472.463 60.5161 465.664 60.5161C461.241 60.5161 457.311 62.1086 453.057 63.0634C448.271 64.1379 443.436 66.2273 439.126 68.5737C436.043 70.252 434.478 73.0186 431.38 71.1729C422.097 65.6428 411.4 62.59 400.969 60.1522C393.366 58.3753 385.939 56.2011 378.486 53.8882C375.869 53.0759 372.986 52.0191 370.299 51.5229C365.08 50.5594 360.093 50.3655 354.963 48.8457C352.7 48.175 352.928 48.2883 349.609 48.2219C343.452 48.0987 331.907 48.2918 326.58 48.3518C318.581 48.4421 310.043 49.0208 301.991 48.4558C301.224 48.4019 300.805 47.1767 300.406 46.6884C298.308 44.1247 295.661 41.8947 293.414 39.4626L293.375 39.4208C289.011 34.6964 284.912 30.2596 279.456 26.8304C276.818 25.1719 274.327 23.3172 271.633 21.7359C265.845 18.3393 260.623 16.2392 254.218 14.6661C244.808 12.355 233.816 12.5031 224.379 14.7701C206.711 19.0144 189.433 26.4434 172.447 32.7566C167.142 34.7284 161.077 36.6699 156.02 39.5145C152.308 41.6024 148.358 43.7465 145.467 46.9483C141.141 51.7399 139.428 57.7505 137.514 63.7911C135.587 69.8688 133.677 73.9406 128.988 78.2947C125.943 81.1224 120.832 84.6329 116.772 85.5465C111.355 86.7654 105.764 87.3457 100.293 88.3277C95.7159 89.1492 89.5545 91.4746 85.9712 94.4878C82.387 97.5018 79.3628 101.394 76.1722 104.807C73.4547 107.713 70.7949 109.524 67.3609 111.487C63.3759 113.764 61.3998 118.389 59.9531 122.533C58.6164 126.363 57.0683 129.037 58.7055 132.878C60.7751 137.734 63.1481 141.639 67.5948 144.627C72.314 147.797 77.9924 149.309 83.6059 149.409C87.8363 149.485 92.1776 150.58 96.446 150.839Z" fill="#D3520A"/>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

@ -1,15 +1,57 @@
<template> <template>
<header>
<h1>Story Baobab</h1>
</header>
<main>
<AddScene @add="add"/> <AddScene @add="add"/>
<section class="scenes">
<SceneEntry
v-for="(scene, index) in scenes"
:order="index+1"
:title="scene.title"
:description="scene.description"
:img="scene.img"
@remove="remove(index)"
/>
</section>
</main>
</template> </template>
<script setup> <script setup>
import AddScene from './components/AddScene.vue' import AddScene from './components/AddScene.vue'
import SceneEntry from './components/SceneEntry.vue'
import {ref} from 'vue'
const scenes = ref([])
const add = (e) => { const add = (e) => {
console.log(e) scenes.value.push(e)
}
const remove = (index) => {
scenes.value.splice(index, 1)
} }
</script> </script>
<style scoped> <style scoped>
header {
margin: 32px;
}
.scenes {
margin: 32px;
display: flex;
flex-wrap: wrap;
gap: 16px;
}
</style> </style>

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

Before

Width:  |  Height:  |  Size: 496 B

@ -1,36 +1,49 @@
<template> <template>
<div class="add-scene"> <div class="add-scene">
<FileLoader ref="thumb"></FileLoader> <FileLoader @upload="getImg" :key="key"></FileLoader>
<div class="meta"> <div class="meta">
<input class="title" v-model="title" placeholder="Title"> <input v-model="title" placeholder="Title">
<textarea class="description" v-model="description" placeholder="Description"></textarea> <textarea v-model="description" placeholder="Description"></textarea>
<button class="insert" @click="add">Add</button> <button class="insert" @click="add">Add</button>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import FileLoader from '../components/FileLoader.vue' import FileLoader from '../components/FileLoader.vue'
import {ref, defineEmits} from 'vue' import {ref} from 'vue'
const title = ref('') const title = ref('')
const description = ref('') const description = ref('')
const thumb = ref(null) const img = ref('')
const key = ref(0)
const emits = defineEmits(['add']) const emits = defineEmits(['add'])
const getImg = (e) => img.value = e
const add = () => { const add = () => {
emits('add', {title: title.value, description: description.value, thumb: thumb.value}) emits('add', {
title: title.value,
description: description.value,
img: img.value}
)
reset()
}
const reset = () => {
title.value = ""
description.value = ""
img.value = ""
key.value += 1
} }
</script> </script>
<style> <style>
.add-scene { .add-scene {
margin: 32px;
display: flex; display: flex;
gap: 8px; gap: 8px;
} }
@ -44,6 +57,7 @@
input, textarea, button { input, textarea, button {
box-sizing: border-box; box-sizing: border-box;
margin: 0;
padding: 8px; padding: 8px;
font-family: sans-serif; font-family: sans-serif;
outline: none; outline: none;

@ -1,12 +1,13 @@
<template> <template>
<div @dragover.prevent @drop.prevent> <div @dragover.prevent @drop.prevent>
<div class="drop" @drop="dragFile"> <div class="drop" @drop="dragFile">
<span> <span v-if="loading" class="loading">
Loading...
</span>
<span v-else>
Drag file here Drag file here
</span> </span>
<component :is="container" ref="thumb" /> <img ref="thumb" />
</div> </div>
</div> </div>
@ -14,11 +15,14 @@
<script setup> <script setup>
import {ref, computed} from 'vue' import {ref, computed} from 'vue'
const emit = defineEmits(['upload'])
const file = ref([]) const file = ref([])
const fileType = ref("") const fileType = ref("")
const thumb = ref(null) const thumb = ref(null)
const loading = ref(false)
const dragFile = (e) => { const dragFile = (e) => {
file.value = e.dataTransfer.files[0] file.value = e.dataTransfer.files[0]
@ -42,16 +46,45 @@
// get file type // get file type
fileType.value = file.type.toLowerCase().substr(0, file.type.indexOf("/")) fileType.value = file.type.toLowerCase().substr(0, file.type.indexOf("/"))
const reader = new FileReader() const reader = new FileReader()
reader.readAsDataURL(file) reader.readAsDataURL(file)
loading.value = true
reader.onloadend = (e) => { reader.onloadend = (e) => {
if (e.target.readyState == FileReader.DONE) { if (e.target.readyState == FileReader.DONE) {
thumb.value.src = filePath if (fileType.value == 'video') videoSnapshot(filePath)
if (fileType.value == 'image') imageSnapshot(filePath)
}
} }
} }
}
const imageSnapshot = (url) => {
thumb.value.src = url
emit('upload', url)
loading.value = false;
}
const videoSnapshot = (url) => {
let video = document.createElement('video')
video.src = url
const snapshot = () => {
let canvas = document.createElement('canvas')
canvas.width = 1280
canvas.height = 720
let ctx = canvas.getContext('2d')
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
thumb.value.src = canvas.toDataURL('image/png')
emit('upload', thumb.value.src)
loading.value = false
video.removeEventListener('canplay', snapshot)
}
video.addEventListener('canplay', snapshot)
}
</script> </script>
@ -75,7 +108,12 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
pointer-events: none;
}
.loading {
opacity: 0.5;
} }
</style> </style>

@ -1,7 +1,9 @@
<template> <template>
<div class="scene"> <div class="scene">
<button class="remove" @click="emit('remove')">x</button>
<img :src="img"> <img :src="img">
<div class="meta"> <div class="meta">
<span class="order">{{order}}</span>
<h3 class="title">{{title}}</h3> <h3 class="title">{{title}}</h3>
<p class="description">{{description}}</p> <p class="description">{{description}}</p>
</div> </div>
@ -11,33 +13,67 @@
</template> </template>
<script setup> <script setup>
import {defineProps} from 'vue'
const props = defineProps(['title', 'description', 'img']) const props = defineProps(['title', 'description', 'img', 'order'])
const emit = defineEmits(['remove'])
</script> </script>
<style> <style>
.scene { .scene {
position: relative;
display: inline-block; display: inline-block;
width: 320px; border: 1px solid currentColor;
height: 180px;
overflow: hidden;
} }
.scene img { .scene img {
position: absolute; width: 320px;
top: 0; height: 180px;
left: 0;
width: 100%;
height: 100%;
object-fit: cover; object-fit: cover;
z-index: 100;
} }
.scene:hover .meta { .meta {
position: relative; position: relative;
z-index: 200; max-width: 320px;
}
.title, .description{
margin-block: 0;
margin-inline: 8px;
}
.description {
font-size: 0.75rem;
margin-bottom: 8px;
}
.order {
position: absolute;
top: 0;
right: 4px ;
display: inline-block;
background-color: white; background-color: white;
z-index: 100;
padding: 4px;
font-weight: bold;
}
.remove {
opacity: 0;
dislay: inline-block;
position: absolute;
background: none;
border: none;
color: white;
right: 4px;
top: 0px;
font-size: 1rem;
font-weight: bold;
cursor: pointer;
}
.scene:hover .remove {
opacity: 1;
} }
</style> </style>

@ -1 +1,13 @@
* {
box-sizing: border-box;
}
html, body {
margin: 0;
}
body {
font-family: sans-serif;
font-size: 16px;
line-height: 1.6;
}

Loading…
Cancel
Save