Difference between revisions of "Test"
(Blanked the page) Tag: Blanking |
|||
Line 1: | Line 1: | ||
+ | #chat-container | ||
+ | #chat-input | ||
+ | #file-input | ||
+ | <style type="text/css" media="screen"> | ||
+ | html, | ||
+ | body { | ||
+ | background-color: hsl(0, 0%, 8%); | ||
+ | // margin: 0; // Go fixed | ||
+ | // font-size: 0; // Fix white-space issues if present. | ||
+ | // height: 100vh; // Go fixed | ||
+ | user-select: none; | ||
+ | } | ||
+ | |||
+ | // For centering | ||
+ | #chat-container { | ||
+ | display: flex; | ||
+ | justify-content: center; | ||
+ | align-items: center; | ||
+ | } | ||
+ | |||
+ | #chat-input { | ||
+ | position: fixed; | ||
+ | bottom: 10px; | ||
+ | height: 40px; | ||
+ | width: 500px; | ||
+ | background-color: hsl(0, 0%, 14%); | ||
+ | border-radius: 6px; | ||
+ | overflow: hidden; | ||
+ | |||
+ | &:before, | ||
+ | &:after { | ||
+ | content: ''; | ||
+ | display: block; | ||
+ | position: absolute; | ||
+ | top: 12px; | ||
+ | bottom: 12px; | ||
+ | background-color: hsla(0, 0%, 22%, 0.4); | ||
+ | } | ||
+ | // Chat input | ||
+ | &:before { | ||
+ | cursor: text; | ||
+ | left: 52px; | ||
+ | width: 40%; | ||
+ | border-radius: 2px; | ||
+ | } | ||
+ | // Emote picker | ||
+ | &:after { | ||
+ | cursor: pointer; | ||
+ | right: 10px; | ||
+ | width: 16px; | ||
+ | border-radius: 8px; | ||
+ | } | ||
+ | #file-input { | ||
+ | cursor: pointer; | ||
+ | display: block; | ||
+ | border-right: 2px solid hsl(0, 0%, 16%); | ||
+ | position: absolute; | ||
+ | top: 2px; | ||
+ | left: 2px; | ||
+ | bottom: 2px; | ||
+ | width: 36px; | ||
+ | transition: background-color 60ms; | ||
+ | |||
+ | &:hover { | ||
+ | top: 0; | ||
+ | left: 0; | ||
+ | bottom: 0; | ||
+ | width: 40px; | ||
+ | background-color: hsl(0, 0%, 28%); | ||
+ | border-right: 0; | ||
+ | transition: background-color 120ms; | ||
+ | } | ||
+ | &:before, | ||
+ | &:after { | ||
+ | content: ''; | ||
+ | display: block; | ||
+ | position: absolute; | ||
+ | background-color: hsl(0, 0%, 22%); | ||
+ | } | ||
+ | &:before { | ||
+ | top: 30%; | ||
+ | bottom: 30%; | ||
+ | left: 50%; | ||
+ | width: 2px; | ||
+ | margin-left: -1px; | ||
+ | } | ||
+ | &:after { | ||
+ | left: 30%; | ||
+ | right: 30%; | ||
+ | top: 50%; | ||
+ | height: 2px; | ||
+ | margin-top: -1px; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | .chat { | ||
+ | position: fixed; | ||
+ | bottom: 60px; | ||
+ | width: 500px; | ||
+ | display: inline-block; | ||
+ | } | ||
+ | |||
+ | .line-container { | ||
+ | overflow: hidden; | ||
+ | border-radius: 6px; | ||
+ | max-height: 0px; | ||
+ | opacity: 0; | ||
+ | transform: translateX(-300px) scale(0.2); | ||
+ | transition: margin-bottom 200ms, max-height 500ms, opacity 100ms, transform 250ms; | ||
+ | transition-timing-function: ease-out; | ||
+ | |||
+ | |||
+ | &:not(:last-child) { | ||
+ | margin-bottom: 10px; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | .line { | ||
+ | padding: 10px; | ||
+ | background-color: hsl(0, 0%, 14%); | ||
+ | |||
+ | & > div { | ||
+ | display: inline-block; | ||
+ | vertical-align: top; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | .profile-img { | ||
+ | cursor: pointer; | ||
+ | border-radius: 6px; | ||
+ | width: 60px; | ||
+ | height: 60px; | ||
+ | background-color: hsl(0, 0%, 22%); | ||
+ | margin-right: 10px; | ||
+ | } | ||
+ | |||
+ | .body { | ||
+ | // background-color: black; | ||
+ | |||
+ | .name, | ||
+ | .text { | ||
+ | border-radius: 2px; | ||
+ | background-color: hsl(0, 0%, 28%); | ||
+ | height: 16px; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | .name { | ||
+ | width: 100px; | ||
+ | margin-bottom: 10px; | ||
+ | position: relative; | ||
+ | cursor: pointer; | ||
+ | |||
+ | &:after { | ||
+ | content: ''; | ||
+ | display: block; | ||
+ | border-radius: 2px; | ||
+ | background-color: hsla(0, 0%, 22%, 0.4); | ||
+ | height: 16px; | ||
+ | width: 50px; | ||
+ | position: absolute; | ||
+ | right: -60px; | ||
+ | transition: 100ms; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | .profile-img:hover + .body .name:after, | ||
+ | .name:hover:after { | ||
+ | background-color: hsla(0, 0%, 22%, 1); | ||
+ | width: 100px; | ||
+ | right: -110px; | ||
+ | } | ||
+ | |||
+ | .text { | ||
+ | &:not(:last-child) { | ||
+ | margin-bottom: 10px; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | .img { | ||
+ | } | ||
+ | |||
+ | .rich-body { | ||
+ | margin-left: 14px; | ||
+ | margin-top: 36px; | ||
+ | position: relative; | ||
+ | |||
+ | &:before { | ||
+ | content: ''; | ||
+ | display: block; | ||
+ | position: absolute; | ||
+ | top: -26px; | ||
+ | left: -14px; | ||
+ | bottom: 0; | ||
+ | width: 4px; | ||
+ | background-color: inherit; | ||
+ | } | ||
+ | &:after { | ||
+ | content: ''; | ||
+ | display: block; | ||
+ | position: absolute; | ||
+ | height: 16px; | ||
+ | width: 200px; | ||
+ | top: -26px; | ||
+ | background-color: inherit; | ||
+ | border-radius: 2px; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | .img, | ||
+ | .rich-body { | ||
+ | width: 300px; | ||
+ | height: 300px; | ||
+ | cursor: pointer; | ||
+ | border-radius: 6px; | ||
+ | background-color: hsl(0, 0%, 20%); | ||
+ | } | ||
+ | |||
+ | .profile-img, | ||
+ | .name, | ||
+ | .text, | ||
+ | .img, | ||
+ | .rich-body { | ||
+ | opacity: 0; | ||
+ | transform: translateY(20px); | ||
+ | transition: 200ms; | ||
+ | } | ||
+ | </style> | ||
+ | <style type="text/javascript" media="screen"> | ||
+ | |||
+ | let amountOfColors = 18; // Or "participants" | ||
+ | |||
+ | let container = document.getElementById('chat-container'); | ||
+ | let lineWidth = 500; | ||
+ | let profileImgWidth = 60; | ||
+ | let textWidth = lineWidth - 20 - profileImgWidth - 10; | ||
+ | let chats = []; | ||
+ | let maxTexts = 4; | ||
+ | |||
+ | function createElement(opts = {}) { | ||
+ | let ele = document.createElement('div'); | ||
+ | if('class' in opts) { | ||
+ | if(!Array.isArray(opts.class)) { | ||
+ | opts.class = [ opts.class ]; | ||
+ | } | ||
+ | ele.classList.add(...opts.class); | ||
+ | } | ||
+ | return ele; | ||
+ | } | ||
+ | |||
+ | function addChat() { | ||
+ | let chat = new Chat(); | ||
+ | chats.push(chat); | ||
+ | setTimeout(() => chat.loop(), 200); | ||
+ | return chat; | ||
+ | } | ||
+ | |||
+ | class Chat { | ||
+ | constructor() { | ||
+ | this.ele = createElement({ class: 'chat' }); | ||
+ | this.lines = []; | ||
+ | this.anim = null; | ||
+ | container.appendChild(this.ele); | ||
+ | } | ||
+ | addLine() { | ||
+ | let l = new Line(); | ||
+ | this.lines.push(l); | ||
+ | this.ele.appendChild(l.ele.lineContainer); | ||
+ | return l; | ||
+ | } | ||
+ | removeOldest() { | ||
+ | let maxCount = Math.ceil(window.innerHeight / 1080 * 12); | ||
+ | if(this.lines.length > maxCount) { | ||
+ | let oldest = this.lines.splice(0, this.lines.length - maxCount); | ||
+ | oldest.forEach(n => this.ele.removeChild(n.ele.lineContainer)); | ||
+ | } | ||
+ | } | ||
+ | loop() { | ||
+ | if(this.anim) { | ||
+ | this.stopLoop(); | ||
+ | } | ||
+ | this.addLine(); | ||
+ | this.removeOldest(); | ||
+ | this.anim = setTimeout(() => this.loop(), Math.random() * 1300 + 180); | ||
+ | } | ||
+ | stopLoop() { | ||
+ | clearTimeout(this.anim); | ||
+ | this.anim = null; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | class Line { | ||
+ | constructor() { | ||
+ | this.pickColor(); | ||
+ | this.pickName(); | ||
+ | this.pickText(); | ||
+ | this.pickHasImg(); | ||
+ | this.pickHasRichBody(); | ||
+ | this.setupElements(); | ||
+ | this.animateIn(); | ||
+ | } | ||
+ | |||
+ | pickColor() { | ||
+ | this.hue = Math.floor(Math.random() * amountOfColors) * (360 / amountOfColors); | ||
+ | this.color = `hsl(${this.hue}, 90%, 50%)`; | ||
+ | this.profileImgColor = `hsl(${this.hue}, 40%, 55%)`; | ||
+ | return this.hue; | ||
+ | } | ||
+ | |||
+ | pickName() { | ||
+ | this.name = Math.max(0.3, Math.random()); | ||
+ | } | ||
+ | |||
+ | pickText() { | ||
+ | let lengthChoice = Math.random(); | ||
+ | let lengthWeight = 1; | ||
+ | if(lengthChoice < 0.5) { | ||
+ | lengthWeight = 0.6; | ||
+ | } | ||
+ | else if(lengthChoice < 0.9) { | ||
+ | lengthWeight = 0.8; | ||
+ | } | ||
+ | this.length = Math.max(0.02, lengthChoice * lengthWeight); | ||
+ | this.textCount = this.length * maxTexts; | ||
+ | } | ||
+ | |||
+ | pickHasImg() { | ||
+ | this.hasImg = Math.random() > 0.9; | ||
+ | } | ||
+ | |||
+ | pickHasRichBody() { | ||
+ | this.hasRichBody = !this.hasImage && Math.random() > 0.85; | ||
+ | } | ||
+ | |||
+ | setupElements() { | ||
+ | let ele = this.createElement(); | ||
+ | this.ele = ele; | ||
+ | ele.name.style.width = this.name * (textWidth / 2) + 'px'; | ||
+ | ele.texts.forEach((n, i, arr) => { | ||
+ | let w = textWidth; | ||
+ | if(i === arr.length - 1) { | ||
+ | w = Math.max(0.2, (this.textCount - i)) * textWidth; | ||
+ | } | ||
+ | n.style.width = w + 'px'; | ||
+ | }); | ||
+ | ele.name.style.backgroundColor = this.color; | ||
+ | ele.profileImg.style.backgroundColor = this.profileImgColor; | ||
+ | } | ||
+ | |||
+ | animateIn() { | ||
+ | let delay = 35; // Some times it won't animate correctly without this | ||
+ | let ele = this.ele; | ||
+ | setTimeout(() => { | ||
+ | ele.lineContainer.style.opacity = 1; | ||
+ | ele.lineContainer.style.maxHeight = '200px'; | ||
+ | ele.lineContainer.style.transform = 'translateX(0px) scale(1)'; | ||
+ | }, delay); | ||
+ | |||
+ | let otherEleList = [ ele.profileImg, ele.name, ...ele.texts ]; | ||
+ | |||
+ | if('img' in ele) { | ||
+ | otherEleList.push(ele.img); | ||
+ | } | ||
+ | else if('richBody' in ele) { | ||
+ | otherEleList.push(ele.richBody); | ||
+ | } | ||
+ | |||
+ | delay += 40; | ||
+ | |||
+ | otherEleList.forEach((e, i) => { | ||
+ | setTimeout(() => { | ||
+ | e.style.opacity = 1; | ||
+ | e.style.transform = 'translateY(0px)'; | ||
+ | }, delay += 50); | ||
+ | }); | ||
+ | |||
+ | ele.texts.forEach((n, i, arr) => setTimeout(() => n.style.opacity = 1, 70 * (i + 3) + delay)); | ||
+ | } | ||
+ | |||
+ | createElement() { | ||
+ | let lineContainer = createElement({ class: 'line-container' }); | ||
+ | let line = createElement({ class: 'line' }); | ||
+ | let profileImg = createElement({ class: 'profile-img' }); | ||
+ | let body = createElement({ class: 'body' }); | ||
+ | let name = createElement({ class: 'name' }); | ||
+ | let texts = []; | ||
+ | let img = createElement({ class: 'img' }); | ||
+ | let richBody = createElement({ class: 'rich-body' }); | ||
+ | body.appendChild(name); | ||
+ | for(let i = 0; i < (this.textCount || 1); i++) { | ||
+ | let text = createElement({ class: 'text' }); | ||
+ | texts.push(text); | ||
+ | body.appendChild(text); | ||
+ | } | ||
+ | line.appendChild(profileImg); | ||
+ | line.appendChild(body); | ||
+ | lineContainer.appendChild(line); | ||
+ | let out = { lineContainer, line, profileImg, body, name, texts }; | ||
+ | this.hasImg && (out.img = img) && body.appendChild(img); | ||
+ | this.hasRichBody && (out.richBody = richBody) && body.appendChild(richBody); | ||
+ | return out; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function loop() { | ||
+ | chats.forEach(n => n.loop()); | ||
+ | } | ||
+ | |||
+ | function stopLoop() { | ||
+ | chats.forEach(n => n.stopLoop()); | ||
+ | } | ||
+ | |||
+ | (() => addChat())(); | ||
+ | |||
+ | </style> |
Revision as of 12:42, 16 December 2022
- chat-container
#chat-input #file-input <style type="text/css" media="screen">
html, body { background-color: hsl(0, 0%, 8%); // margin: 0; // Go fixed // font-size: 0; // Fix white-space issues if present. // height: 100vh; // Go fixed user-select: none; }
// For centering
- chat-container {
display: flex; justify-content: center; align-items: center; }
- chat-input {
position: fixed; bottom: 10px; height: 40px; width: 500px; background-color: hsl(0, 0%, 14%); border-radius: 6px; overflow: hidden;
&:before, &:after { content: ; display: block; position: absolute; top: 12px; bottom: 12px; background-color: hsla(0, 0%, 22%, 0.4); } // Chat input &:before { cursor: text; left: 52px; width: 40%; border-radius: 2px; } // Emote picker &:after { cursor: pointer; right: 10px; width: 16px; border-radius: 8px; } #file-input { cursor: pointer; display: block; border-right: 2px solid hsl(0, 0%, 16%); position: absolute; top: 2px; left: 2px; bottom: 2px; width: 36px; transition: background-color 60ms;
&:hover { top: 0; left: 0; bottom: 0; width: 40px; background-color: hsl(0, 0%, 28%); border-right: 0; transition: background-color 120ms; } &:before, &:after { content: ; display: block; position: absolute; background-color: hsl(0, 0%, 22%); } &:before { top: 30%; bottom: 30%; left: 50%; width: 2px; margin-left: -1px; } &:after { left: 30%; right: 30%; top: 50%; height: 2px; margin-top: -1px; } } }
.chat { position: fixed; bottom: 60px; width: 500px; display: inline-block; }
.line-container { overflow: hidden; border-radius: 6px; max-height: 0px; opacity: 0; transform: translateX(-300px) scale(0.2); transition: margin-bottom 200ms, max-height 500ms, opacity 100ms, transform 250ms; transition-timing-function: ease-out;
&:not(:last-child) {
margin-bottom: 10px;
}
}
.line { padding: 10px; background-color: hsl(0, 0%, 14%);
& > div { display: inline-block; vertical-align: top; } }
.profile-img { cursor: pointer; border-radius: 6px; width: 60px; height: 60px; background-color: hsl(0, 0%, 22%); margin-right: 10px; }
.body { // background-color: black;
.name, .text { border-radius: 2px; background-color: hsl(0, 0%, 28%); height: 16px; } }
.name { width: 100px; margin-bottom: 10px; position: relative; cursor: pointer;
&:after { content: ; display: block; border-radius: 2px; background-color: hsla(0, 0%, 22%, 0.4); height: 16px; width: 50px; position: absolute; right: -60px; transition: 100ms; } }
.profile-img:hover + .body .name:after, .name:hover:after { background-color: hsla(0, 0%, 22%, 1); width: 100px; right: -110px; }
.text { &:not(:last-child) { margin-bottom: 10px; } }
.img { }
.rich-body { margin-left: 14px; margin-top: 36px; position: relative;
&:before { content: ; display: block; position: absolute; top: -26px; left: -14px; bottom: 0; width: 4px; background-color: inherit; } &:after { content: ; display: block; position: absolute; height: 16px; width: 200px; top: -26px; background-color: inherit; border-radius: 2px; } }
.img, .rich-body { width: 300px; height: 300px; cursor: pointer; border-radius: 6px; background-color: hsl(0, 0%, 20%); }
.profile-img, .name, .text, .img, .rich-body { opacity: 0; transform: translateY(20px); transition: 200ms; } </style> <style type="text/javascript" media="screen">
let amountOfColors = 18; // Or "participants"
let container = document.getElementById('chat-container'); let lineWidth = 500; let profileImgWidth = 60; let textWidth = lineWidth - 20 - profileImgWidth - 10; let chats = []; let maxTexts = 4;
function createElement(opts = {}) { let ele = document.createElement('div'); if('class' in opts) { if(!Array.isArray(opts.class)) { opts.class = [ opts.class ]; } ele.classList.add(...opts.class); } return ele; }
function addChat() { let chat = new Chat(); chats.push(chat); setTimeout(() => chat.loop(), 200); return chat; }
class Chat { constructor() { this.ele = createElement({ class: 'chat' }); this.lines = []; this.anim = null; container.appendChild(this.ele); } addLine() { let l = new Line(); this.lines.push(l); this.ele.appendChild(l.ele.lineContainer); return l; } removeOldest() { let maxCount = Math.ceil(window.innerHeight / 1080 * 12); if(this.lines.length > maxCount) { let oldest = this.lines.splice(0, this.lines.length - maxCount); oldest.forEach(n => this.ele.removeChild(n.ele.lineContainer)); } } loop() { if(this.anim) { this.stopLoop(); } this.addLine(); this.removeOldest(); this.anim = setTimeout(() => this.loop(), Math.random() * 1300 + 180); } stopLoop() { clearTimeout(this.anim); this.anim = null; } }
class Line { constructor() { this.pickColor(); this.pickName(); this.pickText(); this.pickHasImg(); this.pickHasRichBody(); this.setupElements(); this.animateIn(); }
pickColor() { this.hue = Math.floor(Math.random() * amountOfColors) * (360 / amountOfColors); this.color = `hsl(${this.hue}, 90%, 50%)`; this.profileImgColor = `hsl(${this.hue}, 40%, 55%)`; return this.hue; }
pickName() { this.name = Math.max(0.3, Math.random()); }
pickText() { let lengthChoice = Math.random(); let lengthWeight = 1; if(lengthChoice < 0.5) { lengthWeight = 0.6; } else if(lengthChoice < 0.9) { lengthWeight = 0.8; } this.length = Math.max(0.02, lengthChoice * lengthWeight); this.textCount = this.length * maxTexts; }
pickHasImg() { this.hasImg = Math.random() > 0.9; }
pickHasRichBody() { this.hasRichBody = !this.hasImage && Math.random() > 0.85; }
setupElements() { let ele = this.createElement(); this.ele = ele; ele.name.style.width = this.name * (textWidth / 2) + 'px'; ele.texts.forEach((n, i, arr) => { let w = textWidth; if(i === arr.length - 1) { w = Math.max(0.2, (this.textCount - i)) * textWidth; } n.style.width = w + 'px'; }); ele.name.style.backgroundColor = this.color; ele.profileImg.style.backgroundColor = this.profileImgColor; }
animateIn() { let delay = 35; // Some times it won't animate correctly without this let ele = this.ele; setTimeout(() => { ele.lineContainer.style.opacity = 1; ele.lineContainer.style.maxHeight = '200px'; ele.lineContainer.style.transform = 'translateX(0px) scale(1)'; }, delay);
let otherEleList = [ ele.profileImg, ele.name, ...ele.texts ];
if('img' in ele) { otherEleList.push(ele.img); } else if('richBody' in ele) { otherEleList.push(ele.richBody); }
delay += 40;
otherEleList.forEach((e, i) => { setTimeout(() => { e.style.opacity = 1; e.style.transform = 'translateY(0px)'; }, delay += 50); });
ele.texts.forEach((n, i, arr) => setTimeout(() => n.style.opacity = 1, 70 * (i + 3) + delay)); }
createElement() { let lineContainer = createElement({ class: 'line-container' }); let line = createElement({ class: 'line' }); let profileImg = createElement({ class: 'profile-img' }); let body = createElement({ class: 'body' }); let name = createElement({ class: 'name' }); let texts = []; let img = createElement({ class: 'img' }); let richBody = createElement({ class: 'rich-body' }); body.appendChild(name); for(let i = 0; i < (this.textCount || 1); i++) { let text = createElement({ class: 'text' }); texts.push(text); body.appendChild(text); } line.appendChild(profileImg); line.appendChild(body); lineContainer.appendChild(line); let out = { lineContainer, line, profileImg, body, name, texts }; this.hasImg && (out.img = img) && body.appendChild(img); this.hasRichBody && (out.richBody = richBody) && body.appendChild(richBody); return out; } }
function loop() { chats.forEach(n => n.loop()); }
function stopLoop() { chats.forEach(n => n.stopLoop()); }
(() => addChat())();
</style>