projection test
This commit is contained in:
parent
cc44e24e60
commit
206d768fcb
3 changed files with 844 additions and 2 deletions
|
|
@ -46,7 +46,7 @@ body::-webkit-scrollbar {
|
|||
height: 100vh;
|
||||
width: 100vw;
|
||||
/* z-index usage is up to you.. although there is no need of using it because the default stack context will work. */
|
||||
z-index: -1; // this is optional
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
|
|
|
|||
29
server.js
29
server.js
|
|
@ -80,11 +80,38 @@ fastify.get("/", async function (request, reply) {
|
|||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//get '/projection'
|
||||
fastify.get("/projection", async function (request, reply) {
|
||||
//get list
|
||||
let list = await fs.readdir(process.env.userdata);
|
||||
list.reverse();
|
||||
|
||||
// console.log(list);
|
||||
|
||||
let folders = [];
|
||||
for (const item of list) {
|
||||
let json = await fs.readFile(process.env.userdata + item + '/fields.json')
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
if (json != undefined) {
|
||||
var fields = JSON.parse(json.toString('utf8'));
|
||||
folders.push({
|
||||
foldername: item,
|
||||
group: fields.group,
|
||||
title: fields.title,
|
||||
comment: fields.comment,
|
||||
anchor: fields.anchor
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(folders);
|
||||
|
||||
//
|
||||
reply.view("/src/pages/parade.html", {
|
||||
reply.view("/src/pages/projection.html", {
|
||||
list: folders
|
||||
});
|
||||
});
|
||||
|
|
|
|||
815
src/pages/projection.html
Normal file
815
src/pages/projection.html
Normal file
|
|
@ -0,0 +1,815 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>흐름을 향하여 걷는 Walking towards the Flow</title>
|
||||
<meta itemprop="description" content="온라인 공간에 소리의 행렬을 만드는 사운드 퍼레이드입니다.">
|
||||
<meta itemprop="image" content="/thumbnail.jpg">
|
||||
<link rel="stylesheet" href="/default.css" />
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: unset;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
#defaultCanvas0 {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.clear {
|
||||
display: block;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
#p5 {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tools {
|
||||
padding: 1em 1em;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.penselect {
|
||||
margin: 1em 0em;
|
||||
}
|
||||
</style>
|
||||
<script src="/js/p5-v1.1.9.min.js"></script>
|
||||
<script src="/js/socket-v2.3.0.io.slim.js"></script>
|
||||
<script src="/js/Tone-14.8.36.min.js"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@500&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="bg"></div>
|
||||
<div id="top"></div>
|
||||
<div class="parade"></div>
|
||||
<div class="entry">
|
||||
<div class="notice">
|
||||
<div class="sticky">
|
||||
<div class="first"><a href="/en/">EN</a></div>
|
||||
<div class="second"><a href="#top">퍼레이드</a></div>
|
||||
</div>
|
||||
<section>
|
||||
<h1>소개</h1>
|
||||
<p>
|
||||
«흐름을 향하여 걷는»은 온라인 공간에서 열리는 사운드 퍼레이드입니다. 이 소리의 행렬은 여러분이 보내주시는 소리가 모여 만들어지며, 누구나 참여하실 수 있습니다.
|
||||
</p>
|
||||
<p>
|
||||
소리가 흐르는 길은 별들이 순회하는 저 은하수와 같이, 언제나 그 자리에 있으면서 여러분이 소리를 듣고 있든 그렇지 않든 흐르고 있습니다.
|
||||
</p>
|
||||
<p>
|
||||
이 영원한 퍼레이드의 영속성은 우리에게 최후의 안도를 줍니다. 때로는 자신이 위성궤도를 무한히 돌고 있는 우주 부스러기라는 생각이 들다가도, 그것이 관측되는 순간 더는 외로움이나 잊혀짐이 아니라
|
||||
기억됨이 됩니다.
|
||||
</p>
|
||||
<p>
|
||||
«흐름을 향하여 걷는»은 온사이트 공연으로 함께 하실 수도 있습니다. 11월 27일(일) 진행하는 공연에서는 웹사이트에 모인 소리들이 물리적 공간에 펼쳐지고, 관객은 함께 걷고 멈추며 흐름에
|
||||
공감해주신 분들의 소리를 듣습니다.
|
||||
</p>
|
||||
<p>
|
||||
온라인으로 진행하는 소리 모집과 온사이트 공연에 많은 관심과 참여 바랍니다.<br />
|
||||
</p>
|
||||
<br>
|
||||
<div class="info">
|
||||
<p>
|
||||
소리 모집<br />
|
||||
2022년 11월 13일(일)–26일(토)
|
||||
</p>
|
||||
<p>
|
||||
공연 일정<br />
|
||||
2022년 11월 27일(일), 16:00
|
||||
</p>
|
||||
<p>
|
||||
공연 장소<br />
|
||||
서울시립미술관 서소문본관 1층 로비, 2층 러닝스테이션, 옥상정원
|
||||
</p>
|
||||
<p>
|
||||
공연 구성<br />
|
||||
무리0 약속들 — 무리1 깃발들 — 무리2 신체들 — 무리3 사물들 — 무리4 누구들
|
||||
</p>
|
||||
<p>
|
||||
공연 신청<br />
|
||||
<a href="https://zrr.kr/aHTS" target="_blank">https://zrr.kr/aHTS</a>
|
||||
</p>
|
||||
<p>
|
||||
작가<br />
|
||||
<a href="http://dianaband.info" target="_blank">다이애나밴드</a> × <a href="https://cgyoon.kr/"
|
||||
target="_blank">윤충근</a>
|
||||
</p>
|
||||
<p>
|
||||
문의<br />wonjung24@gmail.com
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="participation">
|
||||
<h1>참여 방법</h1>
|
||||
<ol>
|
||||
<li>1. 무리의 특성에 따라 녹음기기나 핸드폰으로 30초 가량의 소리를 녹음해주세요.
|
||||
<ol class="category">
|
||||
<li>• 무리1 깃발들 — 좋아하는 것, 가치에 대해 다섯 번 외쳐주세요.</li>
|
||||
<li>• 무리2 신체들 — 몸에서 나는 소리를 녹음해 보아요. 박수, 휘파람도 좋아요.</li>
|
||||
<li>• 무리3 사물들 — 주변 사물들의 소리를 찾아 주세요. 뽁뽁이 소리, 구슬 소리</li>
|
||||
<li>• 무리4 누구들 — 누구의 소리를 모아주세요. 반려동물, 물 소리, 산책의 장소</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>2. 녹음한 소리 파일을 업로드해주세요.</li>
|
||||
<li>3. 소리의 제목과 묘사을 입력한 뒤, 소리의 모양을 그려 제출해주세요.</li>
|
||||
<li>* 혐오 표현이 포함된 음원을 업로드하시면 삭제됩니다.</li>
|
||||
</ol>
|
||||
</section>
|
||||
<section>
|
||||
<h1>소리 제출</h1>
|
||||
<form id="form" method="POST" enctype="multipart/form-data">
|
||||
<ul class="submit">
|
||||
<li>
|
||||
<h3>1. 소리 유형</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<input type="radio" name="group" autocomplete="off" value="flag" /> 무리1 깃발들
|
||||
</li>
|
||||
<li>
|
||||
<input type="radio" name="group" autocomplete="off" value="body" /> 무리2 신체들
|
||||
</li>
|
||||
<li>
|
||||
<input type="radio" name="group" autocomplete="off" value="object" /> 무리3 사물들
|
||||
</li>
|
||||
<li>
|
||||
<input type="radio" name="group" autocomplete="off" value="any" required /> 무리4 누구들
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h3>2. 소리 파일</h3>
|
||||
<input type="file" name="audiofile" autocomplete="off" required />
|
||||
</li>
|
||||
<li>
|
||||
<h3>3. 소리 제목</h3>
|
||||
<input id="title" name="title" type="text" autocomplete="off" required />
|
||||
</li>
|
||||
<li>
|
||||
<h3>4. 소리 묘사</h3>
|
||||
<input id="comment" name="comment" type="text" autocomplete="off" required />
|
||||
</li>
|
||||
<!-- pixels.png ~ made with p5.js -->
|
||||
<li class="noscroll">
|
||||
<h3>5. 소리 모양</h3>
|
||||
<div>
|
||||
<div id="p5">
|
||||
</div>
|
||||
<div class="tools">
|
||||
<div>
|
||||
<input class="penselect" type="radio" value="pencil" name="penselect" id="penselect-pencil"
|
||||
autocomplete="off" checked>
|
||||
<label for="penselect-pencil">연필</label>
|
||||
</div>
|
||||
<div>
|
||||
<input class="penselect" type="radio" value="erasor" name="penselect" id="penselect-erasor"
|
||||
autocomplete="off">
|
||||
<label for="penselect-erasor">지우개</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="clear">다시그리기!</button>
|
||||
</li>
|
||||
<li>
|
||||
<h3>6. 비밀 번호</h3>
|
||||
<input id="pass" name="pass" type="text" maxlength="2" pattern="^\d{2}$" title="암호는 숫자x2개로 해주세요."
|
||||
required />
|
||||
<small>두 자리 숫자를 입력해주세요. 업로드한 소리를 삭제할 때 쓰입니다.</small>
|
||||
</li>
|
||||
<li>
|
||||
<input id="submit" type="submit" value="보내기!" />
|
||||
<progress id="progress" max="100" value="70" style="display:none"></progress>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
↓
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
<section id="list">
|
||||
<section id="list_init_kr" style="display:none">
|
||||
<h1>소리 목록</h1>
|
||||
<p>제출해주신 소리는 아래 목록에 시간순으로 쌓입니다. 각각의 모양을 클릭하면, 모양에 해당하는 소리를 듣고 소리에 대한 정보를 확인할 수 있습니다.</p>
|
||||
</section>
|
||||
<section id="list_init_en" style="display:none">
|
||||
<h1>Sound List</h1>
|
||||
<p>The sounds you submitted will be stacked in chronological order in the list below. By clicking on each shape,
|
||||
you can hear the sound corresponding to the shape and check the information about the sound.</p>
|
||||
</section>
|
||||
{{#each list}}
|
||||
<div class="items" foldername="{{this.foldername}}">
|
||||
<details>
|
||||
<summary><a class="anchor" id="{{this.anchor}}"></a><img class="drawing" src="/uploads/{{this.foldername}}/pixels.png" /></summary>
|
||||
<audio class="sound" preload="none" controls>
|
||||
<source src="/uploads/{{this.foldername}}/audio.mp3" type="audio/mpeg">
|
||||
</audio>
|
||||
<ul class="soundinfo"><hr>
|
||||
<li>유형 | <div class="group">{{this.group}}</div></li><hr>
|
||||
<li>제목 | <div class="title">{{this.title}}</div></li><hr>
|
||||
<li>묘사 | <div class="comment">{{this.comment}}</div></li><hr>
|
||||
</ul>
|
||||
<button class="preview" type="button" onclick="javascript:window.open('/preview/{{this.foldername}}', '_blank');">미리보기</button>
|
||||
<button class="delete" type="button" onclick="del(this)">삭제</button>
|
||||
</details>
|
||||
</div>
|
||||
{{/each}}
|
||||
</section>
|
||||
</div><!-- div class="notice" -->
|
||||
</div><!-- div class="entry" -->
|
||||
|
||||
<script>
|
||||
// // force https
|
||||
// var http_confirm = location.href.split(":")[0];
|
||||
// if (http_confirm == "http") {
|
||||
// window.location.replace("https://" + location.host);
|
||||
// }
|
||||
|
||||
function list_reload() {
|
||||
loadJSON('/list', list => {
|
||||
//if lang == kr
|
||||
let list_init = select('#list_init_kr').html();
|
||||
//if lang == en
|
||||
// let list_init = select('#list_init_en').html();
|
||||
|
||||
//
|
||||
var t = "";
|
||||
list.forEach(item => {
|
||||
t = t + `
|
||||
<div class="items" foldername="${item.foldername}">
|
||||
<details>
|
||||
<summary><a class="anchor" id="${item.anchor}"></a><img class="drawing" src="/uploads/${item.foldername}/pixels.png" /></summary>
|
||||
<audio class="sound" preload="none" controls>
|
||||
<source src="/uploads/${item.foldername}/audio.mp3" type="audio/mpeg">
|
||||
</audio>
|
||||
<ul class="soundinfo"><hr>
|
||||
<li>유형 | <div class="group">${item.group}</div></li><hr>
|
||||
<li>제목 | <div class="title">${item.title}</div></li><hr>
|
||||
<li>묘사 | <div class="comment">${item.comment}</div></li><hr>
|
||||
</ul>
|
||||
<button class="preview" type="button" onclick="javascript:window.open('/preview/${item.foldername}', '_blank');">미리보기</button>
|
||||
<button class="delete" type="button" onclick="del(this)">삭제</button>
|
||||
</details>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
select('#list').html(list_init + t);
|
||||
});
|
||||
};
|
||||
|
||||
var socket = io(location.host);
|
||||
var n = 0;
|
||||
var fr = 20;
|
||||
var arr = [];
|
||||
var looper;
|
||||
var score;
|
||||
var logo;
|
||||
var silence;
|
||||
var clap;
|
||||
|
||||
//promisify -> new Tone.Player
|
||||
function AudioImport(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var audio = new Tone.Player(url, () => resolve(audio));
|
||||
});
|
||||
}
|
||||
|
||||
//**ENTRY begin >>>
|
||||
// -- https://stackoverflow.com/a/39577640
|
||||
function dataURLtoBlob(dataURL) {
|
||||
let array, binary, i, len;
|
||||
binary = atob(dataURL.split(",")[1]);
|
||||
array = [];
|
||||
i = 0;
|
||||
len = binary.length;
|
||||
while (i < len) {
|
||||
array.push(binary.charCodeAt(i));
|
||||
i++;
|
||||
}
|
||||
return new Blob([new Uint8Array(array)], {
|
||||
type: "image/png",
|
||||
});
|
||||
}
|
||||
|
||||
function submitForm(event) {
|
||||
|
||||
//TODO : first check if there is a drawing or not. (pixels)
|
||||
|
||||
//
|
||||
var submit = document.getElementById("submit");
|
||||
submit.setAttribute('disabled', 'disabled');
|
||||
select("html").style('cursor', 'progress');
|
||||
|
||||
//
|
||||
var form = document.getElementById("form");
|
||||
var fd = new FormData(form);
|
||||
|
||||
//
|
||||
var unit = 45; //mask&override 'unit' -> for crispy png.
|
||||
var img2 = createGraphics(cols * unit + 2, rows * unit + 2);
|
||||
img2.clear();
|
||||
img2.strokeWeight(0);
|
||||
img2.fill(255);
|
||||
for (var c = 0; c < cols; c++) {
|
||||
for (var r = 0; r < rows; r++) {
|
||||
if (p[c * cols + r] == 1) {
|
||||
img2.rect(c * unit + 1, r * unit + 1, unit, unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
var dataurl = img2.elt.toDataURL();
|
||||
var pixels = dataURLtoBlob(dataurl);
|
||||
fd.append("pixels", pixels, "pixels.png");
|
||||
|
||||
for (var pair of fd.entries()) {
|
||||
console.log(pair[0] + ', ' + pair[1]);
|
||||
}
|
||||
|
||||
//
|
||||
var request = new XMLHttpRequest();
|
||||
//
|
||||
var progress = select("#progress");
|
||||
progress.style('display', 'unset');
|
||||
//
|
||||
request.open("POST", "/");
|
||||
request.upload.onprogress = (pe) => {
|
||||
if (pe.lengthComputable) {
|
||||
progress.elt.max = pe.total;
|
||||
progress.elt.value = pe.loaded;
|
||||
}
|
||||
}
|
||||
request.onload = function () {
|
||||
console.log('received http response');
|
||||
console.log('response status: ', request.status);
|
||||
console.log('response headers: ', request.getAllResponseHeaders());
|
||||
console.log('response body: ', request.response);
|
||||
}
|
||||
request.onloadend = () => {
|
||||
// check server's final feedback here.
|
||||
if (request.status == 200) {
|
||||
alert('감사합니다. 아래에 소리목록에서 업로드된 음원을 확인해보세요.');
|
||||
} else {
|
||||
alert('앗! 소리 파일 전송에 문제가 생겼어요\n wonjung24@gmail.com에 소리 파일을 한번 더 보내주시면 추가하겠습니다.\n 관리자 정보: 서버 응답 [' + request.status + ']');
|
||||
}
|
||||
// re-enable next upload.
|
||||
submit.removeAttribute('disabled');
|
||||
select("html").style('cursor', 'auto');
|
||||
form.reset();
|
||||
for (let a = 0; a < p.length; a++) p[a] = 0; //clear pixel-drawing, too.
|
||||
// reload list.
|
||||
list_reload();
|
||||
}
|
||||
request.send(fd);
|
||||
|
||||
//
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
const form = document.getElementById('form');
|
||||
form.addEventListener('submit', submitForm);
|
||||
|
||||
//// ---- for 'list' rendering ----
|
||||
|
||||
function del(that) {
|
||||
console.log();
|
||||
let text;
|
||||
let pass = prompt("패스워드를 맞춰보세요!", "숫자2개");
|
||||
if (/^\d{2}$/.test(pass)) {
|
||||
var target = that.parentElement.parentElement.getAttribute('foldername');
|
||||
const trydelete = async () => {
|
||||
const response = await fetch('/delete/' + target + '/' + pass);
|
||||
if (response.ok) {
|
||||
const json = await response.json();
|
||||
return Promise.resolve(json);
|
||||
} else {
|
||||
return Promise.reject('no response.');
|
||||
}
|
||||
}
|
||||
trydelete().then((resp) => {
|
||||
if (resp.result) {
|
||||
alert("지우기 성공!");
|
||||
// location.reload();
|
||||
list_reload();
|
||||
} else {
|
||||
alert("지우기 실패-");
|
||||
}
|
||||
}).catch(console.log);
|
||||
} else {
|
||||
alert("암호는 숫자2개...");
|
||||
}
|
||||
}
|
||||
|
||||
var p = [];
|
||||
var cols = 17;
|
||||
var rows = 17;
|
||||
var unit = 15; //px
|
||||
var img;
|
||||
var penselect = "pencil";
|
||||
var scrollable = true;
|
||||
// <<< end of **ENTRY
|
||||
|
||||
async function setup() {
|
||||
|
||||
//**PARADE
|
||||
noCanvas();
|
||||
if (windowWidth > 1500 && windowWidth > windowHeight) {
|
||||
fr = 120;
|
||||
} else {
|
||||
fr = 20;
|
||||
}
|
||||
frameRate(fr);
|
||||
|
||||
//
|
||||
|
||||
|
||||
//p5 'draw()' doesn't work if user is not looking at the tab.
|
||||
//noLoop(); // <-- BUT, we want 1 for ENTRY graphics!
|
||||
// --> use custom looper.
|
||||
|
||||
//**ENTRY
|
||||
// list_reload();
|
||||
|
||||
// for korean page...
|
||||
var list_intro = select("#list_init_kr");
|
||||
list_intro.style('display', 'unset');
|
||||
|
||||
var cnv = createCanvas(cols * unit + 2, rows * unit + 2);
|
||||
cnv.parent("p5");
|
||||
img = createGraphics(cols * unit + 2, rows * unit + 2);
|
||||
document.querySelectorAll('.penselect').forEach(item => item.onclick = () => {
|
||||
penselect = document.querySelector('input[name="penselect"]:checked').value;
|
||||
});
|
||||
//
|
||||
for (var i = 0; i < cols * rows; i++) p.push(0);
|
||||
document.querySelectorAll('.clear').forEach(item => item.onclick = () => {
|
||||
for (let a = 0; a < p.length; a++) p[a] = 0;
|
||||
});
|
||||
|
||||
//go to the anchor
|
||||
console.log(window.location.hash.substring(1));
|
||||
let elmnt = document.getElementById(window.location.hash.substring(1));
|
||||
if (elmnt) {
|
||||
elmnt.scrollIntoView(true);
|
||||
}
|
||||
}
|
||||
|
||||
//**ENTRY begin >>>
|
||||
function draw() {
|
||||
//clear
|
||||
clear();
|
||||
|
||||
//draw the grid
|
||||
stroke(255);
|
||||
strokeWeight(0.2);
|
||||
for (var c = 0; c < cols; c++) {
|
||||
for (var r = 0; r < rows; r++) {
|
||||
noFill();
|
||||
rect(c * unit + 1, r * unit + 1, unit, unit);
|
||||
}
|
||||
}
|
||||
|
||||
//pointer
|
||||
fill(0, 255, 0);
|
||||
strokeWeight(0);
|
||||
|
||||
//mouse way
|
||||
circle(mouseX, mouseY, 10);
|
||||
if (mouseIsPressed && mouseButton === LEFT) {
|
||||
//find slot under the pointer
|
||||
var mouseC = int(mouseX / unit);
|
||||
var mouseR = int(mouseY / unit);
|
||||
if (mouseC >= 0 && mouseC < cols && mouseR >= 0 && mouseR < rows) {
|
||||
if (penselect == "pencil") p[mouseC * cols + mouseR] = 1;
|
||||
else if (penselect == "erasor") p[mouseC * cols + mouseR] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//touch way
|
||||
if (touches.length > 0) {
|
||||
circle(touches[0].x, touches[0].y, 10);
|
||||
//find slot under the pointer
|
||||
var mouseC = int(touches[0].x / unit);
|
||||
var mouseR = int(touches[0].y / unit);
|
||||
if (mouseC >= 0 && mouseC < cols && mouseR >= 0 && mouseR < rows) {
|
||||
// if (scrollable == true) {
|
||||
// scrollable = false;
|
||||
// // firefox browser @ my android phone -> url bar auto-hiding kills drawing experience.
|
||||
// // --> https://stackoverflow.com/a/63221105
|
||||
// document.body.style.marginTop = `-${window.pageYOffset}px`;
|
||||
// document.body.style.position = 'fixed';
|
||||
// document.body.style.overflowY = 'scroll';
|
||||
// }
|
||||
if (penselect == "pencil") p[mouseC * cols + mouseR] = 1;
|
||||
else if (penselect == "erasor") p[mouseC * cols + mouseR] = 0;
|
||||
} else {
|
||||
// if (scrollable == false) {
|
||||
// scrollable = true;
|
||||
// // firefox browser @ my android phone -> url bar auto-hiding kills drawing experience.
|
||||
// // --> https://stackoverflow.com/a/63221105
|
||||
// document.body.style.position = '';
|
||||
// document.body.style.overflowY = '';
|
||||
// if (document.body.style.marginTop) {
|
||||
// const scrollTop = -parseInt(document.body.style.marginTop, 10);
|
||||
// document.body.style.marginTop = '';
|
||||
// window.scrollTo(window.pageXOffset, scrollTop);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
//draw img
|
||||
image(img, 0, 0);
|
||||
img.clear();
|
||||
img.strokeWeight(0);
|
||||
img.fill(255);
|
||||
for (var c = 0; c < cols; c++) {
|
||||
for (var r = 0; r < rows; r++) {
|
||||
if (p[c * cols + r] == 1) {
|
||||
img.rect(c * unit + 1, r * unit + 1, unit, unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// <<< end of **ENTRY
|
||||
|
||||
//
|
||||
var myroom = -1;
|
||||
var intro;
|
||||
var ready;
|
||||
//
|
||||
socket.on("connect", async function () {
|
||||
console.log("connected!");
|
||||
setTimeout(gotConnection, 1000);
|
||||
console.log("- injecting small delay to make sure p5js loaded. ~ 1s");
|
||||
});
|
||||
|
||||
async function gotConnection() {
|
||||
//
|
||||
console.log("- after delay!");
|
||||
//
|
||||
silence = (await AudioImport("./audio/_silence.wav")).toDestination();
|
||||
clap = (await AudioImport("./audio/clap01.mp3")).toDestination();
|
||||
|
||||
//TESTING... fixed to room 1.
|
||||
// myroom = 1;
|
||||
|
||||
if (myroom == -1 && selectAll(".roomsel").length == 0) {
|
||||
//initial connection -> ask the room number.
|
||||
|
||||
var roomsel = createDiv();
|
||||
selectAll(".parade")[0].child(roomsel);
|
||||
roomsel.class("roomsel");
|
||||
///
|
||||
var b = createButton("시작하기 Start!"); //, "1");
|
||||
var d = createDiv("흐름을 향하여 걷는 Walking towards the Flow");
|
||||
d.class("title");
|
||||
let starter = function () {
|
||||
silence.start();
|
||||
clap.start();
|
||||
// myroom = parseInt(this.value());
|
||||
myroom = 1;
|
||||
|
||||
socket.emit("room", myroom, function (res) {
|
||||
if (res) {
|
||||
console.log("entered the room -> " + myroom);
|
||||
|
||||
// myroom-indicator
|
||||
// var tag = createP(str(myroom));
|
||||
// selectAll(".parade")[0].child(tag);
|
||||
|
||||
//
|
||||
// setTimeout(function() {
|
||||
// ready = createP("퍼레이드 시작합니다!!");
|
||||
// ready.position(
|
||||
// windowWidth / 2 - windowWidth / 10,
|
||||
// windowHeight / 2
|
||||
// );
|
||||
// }, 1000);
|
||||
} else {
|
||||
console.log("rejected!");
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
selectAll(".roomsel").forEach(item => {
|
||||
item.remove();
|
||||
// 1 second popup '.intro' div
|
||||
intro = createDiv(
|
||||
"«흐름을 향하여 걷는» 소리 행렬이 곧 나타납니다. <br>오른쪽에서 왼쪽으로 지나가는 소리가 들리지 않는다면 볼륨을 확인해주시고, 스마트폰 환경에서는 진동 모드를 해제해주세요. <br>스크롤을 내리면 참여 방법을 확인하고 소리를 업로드할 수 있으며, 스크롤을 맨 위로 올리거나 오른쪽 위의 글씨를 클릭해 퍼레이드로 다시 돌아올 수 있습니다. <br><br>The sound parade «Walking towards the Flow», appears soon. <br>If you can't hear the sound passing from right to left, check the volume, and if you're using a smartphone, turn off the vibrate mode. <br>Scroll down to see how to join and upload sound, scroll to the top or click the text “parade” on the top right to return to the parade.<br><img class='arrow' src='arrow.svg'>"
|
||||
);
|
||||
intro.class("notice intro"); //-> fadeout & disapear by css animation style.
|
||||
});
|
||||
}, 1000);
|
||||
};
|
||||
b.mouseClicked(starter);
|
||||
d.mouseClicked(starter);
|
||||
roomsel.child(d);
|
||||
roomsel.child(b);
|
||||
} else {
|
||||
//re-connection -> just connect to remembered room!
|
||||
socket.emit("room", myroom, function (res) {
|
||||
if (res) {
|
||||
console.log("entered the room -> " + myroom);
|
||||
} else {
|
||||
console.log("rejected!");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//var fading_factor = 0.3; //30%
|
||||
var fading_factor = 0.5; //50%
|
||||
socket.on("post", async function (post) {
|
||||
|
||||
console.log(post);
|
||||
var list = await new Promise((resolve, reject) => {
|
||||
loadJSON("/entries", (json) => resolve(json));
|
||||
})
|
||||
console.log(list);
|
||||
|
||||
// var object = post.object;
|
||||
var object = {
|
||||
"id": 1,
|
||||
"type": "abc",
|
||||
"src": "/uploads/" + list[post] + "/pixels.png",
|
||||
"audio": "/uploads/" + list[post] + "/audio.mp3",
|
||||
"alt": "알트",
|
||||
"size": {
|
||||
"base": 40,
|
||||
"random": 20
|
||||
},
|
||||
"y": {
|
||||
"base": 40,
|
||||
"random": 25
|
||||
},
|
||||
"showtime": 40000
|
||||
};
|
||||
console.log(object);
|
||||
|
||||
var img = createImg(object.src, object.alt, "", async function (im) {
|
||||
//로딩이 끝나면, start!
|
||||
var pv = new Tone.PanVol(0, -99).toDestination();
|
||||
var snd = await AudioImport(object.audio); // NOTE: url with spaces didn't work here.
|
||||
snd.connect(pv).start();
|
||||
snd.loop = true;
|
||||
|
||||
//로딩이 끝나면, show!
|
||||
im.show();
|
||||
|
||||
//그림의 크기와 초기 위치 ==> 가로 보기인 경우
|
||||
var width = 0;
|
||||
if (windowWidth > windowHeight) {
|
||||
|
||||
width = (windowHeight * (object.size.base * 0.4 + object.size.random * Math.random())) / 100; // 좀더 크게 + 40% (ratio)
|
||||
im.size(width, AUTO);
|
||||
|
||||
im.position(
|
||||
windowWidth * (1 + fading_factor),
|
||||
(windowHeight * (object.y.base + object.y.random * Math.random()) * 0.5) / 100 // 좀더 위로 위로 - 50% (ratio)
|
||||
);
|
||||
|
||||
//그림의 크기와 초기 위치 ==> 세로 보기인 경우
|
||||
} else {
|
||||
|
||||
width = (windowHeight * (object.size.base + object.size.random * Math.random())) / 100; // json에서 정한 크기 그대로. (ratio)
|
||||
im.size(width, AUTO);
|
||||
|
||||
im.position(
|
||||
windowWidth * (1 + fading_factor),
|
||||
(windowHeight * (object.y.base + object.y.random * Math.random())) / 100 // json에서 정한 위치 그대로. (ratio)
|
||||
);
|
||||
}
|
||||
|
||||
//추가 정보들
|
||||
im.attribute("data-type", object.type);
|
||||
im.attribute("data-showtime", object.showtime / 1000); //milli-sec. -> seconds.
|
||||
|
||||
//'아이콘' 들은 애니메이션을 시켜줘야 함...
|
||||
if (object.type == "icon") {
|
||||
//
|
||||
im.class("rotate");
|
||||
im.style("animation-duration", object.rotate + "s");
|
||||
var orgs = im.style("transform-origin").split(" ");
|
||||
var str = parseFloat(orgs[0]) + object.pivot.x + "px";
|
||||
str = str + " " + parseFloat(orgs[1]) + object.pivot.y + "px";
|
||||
im.style("transform-origin", str);
|
||||
//
|
||||
}
|
||||
|
||||
//로딩이 다 되면, rendering array에 추가.
|
||||
var bundle = {
|
||||
ref: object,
|
||||
img: im,
|
||||
sound: snd,
|
||||
panvol: pv,
|
||||
width: width
|
||||
};
|
||||
arr.push(bundle);
|
||||
});
|
||||
|
||||
//첨에는 hide
|
||||
img.hide();
|
||||
|
||||
});
|
||||
|
||||
//p5 'draw()' doesn't work if user is not looking at the tab.
|
||||
// --> custom looper is ok.
|
||||
var looper;
|
||||
(looper = function (timeout) {
|
||||
setTimeout(async function () {
|
||||
|
||||
//
|
||||
for (var i = arr.length - 1; i >= 0; i -= 1) {
|
||||
var bundle = arr[i];
|
||||
var img = bundle.img;
|
||||
var showtime = parseFloat(img.attribute("data-showtime"));
|
||||
var type = img.attribute("data-type");
|
||||
var x = img.position().x;
|
||||
var y = img.position().y;
|
||||
y = y + random(-1, 1);
|
||||
x = x - windowWidth / (fr * showtime);
|
||||
|
||||
//
|
||||
if (type == "icon") {
|
||||
img.style("z-index", "-1");
|
||||
}
|
||||
3;
|
||||
|
||||
img.position(x, y);
|
||||
var pan = (x / windowWidth) * 2 - 1;
|
||||
|
||||
//panning
|
||||
var snd = bundle.sound;
|
||||
var pv = bundle.panvol;
|
||||
if (x >= -bundle.width && x < windowWidth) {
|
||||
pan = ((x + bundle.width) / (windowWidth + bundle.width)) * 2 - 1;
|
||||
pv.pan.value = pan;
|
||||
pv.volume.value = 0;//(dB)
|
||||
} else {
|
||||
var range;
|
||||
var knob;
|
||||
if (x >= windowWidth) {
|
||||
range = windowWidth * fading_factor
|
||||
knob = x - windowWidth;
|
||||
pv.pan.value = 1;
|
||||
pv.volume.value = knob / range * -20;
|
||||
} else if (x < -bundle.width) {
|
||||
range = windowWidth * fading_factor
|
||||
knob = (x + bundle.width) * -1;
|
||||
pv.pan.value = -1;
|
||||
pv.volume.value = knob / range * -20;
|
||||
}
|
||||
}
|
||||
|
||||
//scroll down -> volume down
|
||||
// if (userscroll > windowHeight*2) {
|
||||
// pv.volume.value = 0; //off
|
||||
// } else if (userscroll >= windowHeight && userscroll < windowHeight*2) {
|
||||
// pv.volume.value = (1 - (userscroll - windowHeight)/windowHeight) * pv.volume.value; //fade out
|
||||
// }
|
||||
|
||||
//remove with sound fade-out
|
||||
var exit_x = -bundle.width - windowWidth * fading_factor;
|
||||
if (x < exit_x) {
|
||||
img.remove();
|
||||
snd.stop();
|
||||
delete snd;
|
||||
delete pv;
|
||||
arr.splice(i, 1);
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
looper(1000 / fr);
|
||||
}, timeout);
|
||||
})(1000 / fr);
|
||||
|
||||
// function randomvoiceplay() {
|
||||
// (looper = function(timeout) {
|
||||
// setTimeout(function() {
|
||||
// voice[int(random(19))].play();
|
||||
// looper(random(8000, 12000));
|
||||
// }, timeout);
|
||||
// })(8000);
|
||||
// }
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Loading…
Reference in a new issue