|
|
(11 intermediate revisions by the same user not shown) |
Line 1: |
Line 1: |
− | #chat-container
| + | {{triDRender |
− | #chat-input
| + | |disp_widget={{#widget:RuneDisp|runefile=/images/7/7a/Runecraft.glb|multilayer=false}} |
− | #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>
| |