i have provided all my codes here all seems good the problem is when i change from last slide to first or the opposite in both case all slides flips differently instead rotating like with other sildes
render() function is the place where all co-ordinates are calculating
i think the issue is with CSS transition since its the main thing which is animating slides, i dont see any easy fix for this if you can find any tell me please looking for easy solution or possibility without messing too much with co-ordinates . thanks in advance
class slider {
constructor(){
if(typeof arguments[0] != "undefined"){
this.init()
arguments[0].appendChild(this.html)
this.calculate()
this.render(1)
}
else{this.init()}
}
init(){
let obj = this
this.html = `
<div class="root border-2 p-5 border-black m-auto">
<div class="each border border-black absolute text-center">item name 1</div>
</div>
`
let el = document.createElement("div")
el.innerHTML = this.html
this.html = el.querySelector("*")
this.root = this.html
let eEl = this.root.querySelector(".each")
this.eachTemp = eEl.cloneNode(true)
eEl.remove()
this.fillBoxes(6)
this.html.addEventListener("click",function(e){
obj.onclick(obj,e)
})
}
onclick(obj,e){
let t = e.target
if(t.closest(".each")){
obj.render(t.index)
}
}
fillBoxes(num){
for(let i=0;i<num;i++){
let newEl = this.eachTemp.cloneNode(true)
newEl.index = i+1
newEl.innerText = "Item " + (i+1)
this.html.appendChild(newEl)
}
this.els = this.html.querySelectorAll(".each")
}
calculate(){
this.eCor = this.els[0].getClientRects()[0]
this.mag = this.eCor.width
this.per = Math.PI / (this.els.length / 2)
this.deg = this.rtod(this.per)
}
render(index){
this.els.forEach((each,i)=>{
let rad = (this.per * i) - this.per*(index-1),
x = Math.sin(rad)*this.mag,
y = Math.cos(rad)*this.mag
if(i+1 == index){each.classList.add("active")}
else{each.classList.remove("active")}
each.style.transform = `translate3d(${x}px,0%,${y}px)`
each.style.transform += `rotateY(${this.rtod(rad)}deg)`
})
}
rtod(radians){
return radians * (180/Math.PI)
}
}
const s = new slider(document.body)
.each{
height: 250px;
width: 250px;
background: skyblue;
opacity: 0.8;
transform-origin: 50% 50%;
transform-style: preserve-3d;
user-select: none;
filter: brightness(80%);
transition: all 1s cubic-bezier(0, 1.22, 0.53, 1.02);
/* box-shadow: 0px 0px 10px 1px black; */
}
.each:nth-child(odd){background:lightsalmon}
.root{
height: 280px;
width: 280px;
position: relative;
margin-top: 10%;
perspective: 1000px;
transform-style: preserve-3d;
transition: transform 0.5s;
/* animation: loop 4s infinite linear; */
}
.each:hover{
z-index: 1000 !important;
filter:brightness(110%);
}
.active{
opacity: 1 !important;
filter:brightness(100%) !important;
}
<link href="https://unpkg.com/tailwindcss#^2/dist/tailwind.min.css" rel="stylesheet">
When you switch between 1 and 6 slides, your calculated rotatedY value changes from 0deg to 300deg (or from 300deg to 0deg). But actually you need only ±60deg update. So you need a mechanism to increase/decrease your rotatedY endlessly in both ways, for example 240 -> 300 -> 360 -> 420 -> 480 ... (not 240 -> 300 -> 0 -> 60 -> ...)
I suggest you to have these properties in your class:
currentIndex = 1;
radOffset = 0;
Then, when you click on element, you need to calculate radOffset (it increases/decreases endlessly):
const totalElements = this.els.length;
const rightDiff = t.index > this.currentIndex ? t.index - this.currentIndex : totalElements + t.index - this.currentIndex;
const leftDiff = totalElements - rightDiff;
const closestDiff = rightDiff < leftDiff ? - rightDiff : leftDiff;
this.currentIndex = t.index;
this.radOffset += this.per * closestDiff;
Then use it in your render function:
let rad = (this.per * i) + this.radOffset,
class slider {
currentIndex = 1;
radOffset = 0;
rotateOffset = 0;
constructor(){
if(typeof arguments[0] != "undefined"){
this.init()
arguments[0].appendChild(this.html)
this.calculate()
this.render(1)
}
else{this.init()}
}
init(){
let obj = this
this.html = `
<div class="root border-2 p-5 border-black m-auto">
<div class="each border border-black absolute text-center">item name 1</div>
</div>
`
let el = document.createElement("div")
el.innerHTML = this.html
this.html = el.querySelector("*")
this.root = this.html
let eEl = this.root.querySelector(".each")
this.eachTemp = eEl.cloneNode(true)
eEl.remove()
this.fillBoxes(6)
this.html.addEventListener("click",function(e){
obj.onclick(obj,e)
})
}
onclick(obj,e){
let t = e.target
if(t.closest(".each")){
const totalElements = this.els.length;
const rightDiff = t.index > this.currentIndex ? t.index - this.currentIndex : totalElements + t.index - this.currentIndex;
const leftDiff = totalElements - rightDiff;
const closestDiff = rightDiff < leftDiff ? - rightDiff : leftDiff;
this.currentIndex = t.index;
this.radOffset += this.per * closestDiff;
obj.render(t.index);
}
}
fillBoxes(num){
for(let i=0;i<num;i++){
let newEl = this.eachTemp.cloneNode(true)
newEl.index = i+1
newEl.innerText = "Item " + (i+1)
this.html.appendChild(newEl)
}
this.els = this.html.querySelectorAll(".each")
}
calculate(){
this.eCor = this.els[0].getClientRects()[0]
this.mag = this.eCor.width
this.per = Math.PI / (this.els.length / 2)
this.deg = this.rtod(this.per)
}
render(index, rotateDiff){
this.els.forEach((each,i)=>{
let rad = (this.per * i) + this.radOffset,
x = Math.sin(rad)*this.mag,
y = Math.cos(rad)*this.mag
if(i+1 == index){each.classList.add("active")}
else{each.classList.remove("active")}
each.style.transform = `translate3d(${x}px,0%,${y}px)`
each.style.transform += `rotateY(${this.rtod(rad)}deg)`
})
}
rtod(radians){
return Math.round(radians * (180/Math.PI));
}
}
const s = new slider(document.body)
.each{
height: 250px;
width: 250px;
background: skyblue;
opacity: 0.8;
transform-origin: 50% 50%;
transform-style: preserve-3d;
user-select: none;
filter: brightness(80%);
transition: all 1s cubic-bezier(0, 1.22, 0.53, 1.02);
/* box-shadow: 0px 0px 10px 1px black; */
}
.each:nth-child(odd){background:lightsalmon}
.root{
height: 280px;
width: 280px;
position: relative;
margin-top: 10%;
perspective: 1000px;
transform-style: preserve-3d;
transition: transform 0.5s;
/* animation: loop 4s infinite linear; */
}
.each:hover{
z-index: 1000 !important;
filter:brightness(110%);
}
.active{
opacity: 1 !important;
filter:brightness(100%) !important;
}
<link href="https://unpkg.com/tailwindcss#^2/dist/tailwind.min.css" rel="stylesheet">
Related
I have a small 3D cube written in CSS and JavaScript / Angular that rotates on one axis using the "transform: rotateX" method.
It's designed so that when it's clicked on, it rotates up or down in the mouse's direction - this works fairly well. rotateX is powered by a torque control and a rotation value.
However, when released I'd like it to come to a smooth stop on the "camera-facing" side. This.. is very much a hit or miss. Sometimes it works smoothly, others it decides to jump to the next panel on the cube.
At the moment, I have hardcoded detecting which side it's on but there's probably a better way - this might be part of the problem. With the code I have now, is there a better method to get the stop effect?
In Action:
Video of rolling dice under user control
HTML:
<div class="dice"
(mousedown)="mouseDown($event)"
(dblclick)="doubleClick()"
>
<div class="cube" [ngStyle]="{'transform': 'rotateX(' + rotation + 'deg)'}">
<div #pane class="face front">4</div>
<div #pane class="face bottom">3</div>
<div #pane class="face back">2</div>
<div #pane class="face top">1</div>
</div>
JavaScript:
import { Component, OnInit, HostListener, ElementRef, ViewChildren, QueryList } from '#angular/core';
#Component({
selector: 'roller',
templateUrl: './roller.component.html',
styleUrls: ['./roller.component.scss']
})
export class RollerComponent implements OnInit {
//#ts-ignore
#ViewChildren("pane", { static: false }) diePanels: QueryList<ElementRef>;
controlDown: boolean = false;
rotationSpeed: number = 2;
friction: number = 0.1;
frictionCutoff: number = 0.93;
mouseY: number = 0;
lastY: number = 0;
delta: number = 0;
torque: number = 0;
rotation: number = 0;
lastSide: number = 0;
currentSide: number = 0;
targetMovement: number = 0;
torqueTarget: number = 0;
torqueMaxSpeed: number = 8;
stopAt: number = 0;
// Get an array of all dice panels //
panels: ElementRef[] = [];
constructor() { }
ngOnInit(): void {
// Lifecycle Placeholder //
}
ngAfterViewInit()
{
this.panels = this.diePanels.toArray();
// In case a reference to the parent div is needed //
// let parent = this.panels[0].nativeElement.parentNode.parentNode;
}
mouseDown(e: any) {
this.controlDown = true;
this.mouseY = e.pageY;
this.lastY = this.mouseY;
}
#HostListener('document:mouseup', ['$event'])
mouseUp()
{
// Listen for mouse up when not on div //
this.controlDown = false;
}
#HostListener('document:mousemove', ['$event'])
onMouseMove(e) {
if (this.controlDown)
{
// Listen for mouse movement //
this.mouseY = e.pageY;
}
}
doubleClick()
{
// Double Click Test //
console.log(this.controlDown);
}
animate(timeDelta: number)
{
// Capture the difference in the click position and current mouse pos
this.delta = this.mouseY - this.lastY;
// Clamp Rotation to 0-360 //
if(this.rotation > 360) {
this.rotation = 0;
} else if(this.rotation < 0) {
this.rotation = 360;
}
// Control Dice when mouse is clicked on it
if (this.controlDown)
{
this.torque = this.torque * this.frictionCutoff + (this.delta * this.rotationSpeed - this.torque) * this.friction;
// Limit Torque Speed
if (this.torque >= this.torqueMaxSpeed)
{
this.torque = this.torqueMaxSpeed;
} else if (this.torque <= -this.torqueMaxSpeed) {
this.torque = -this.torqueMaxSpeed;
}
this.rotation -= this.torque;
} else {
// Slow dice and stop //
this.delta = 0;
this.lastY = 0;
this.torque = 0;
let rot = Math.floor(this.rotation);
// Hardcoding to detect which side is "facing" the viewer //
if (rot > 0 && rot < 90)
{
this.torqueTarget = 90;
this.currentSide = 0;
} else if (rot > 90 && rot < 180) {
this.torqueTarget = 180;
this.currentSide = 1;
} else if (rot > 180 && rot < 270) {
this.torqueTarget = 270;
this.currentSide = 2;
} else if (rot > 270 && rot < 360) {
this.torqueTarget = 360;
this.currentSide = 3;
}
this.rotation = this.lerp(this.rotation, this.torqueTarget, 0.5);
}
}
lerp(start: number, end: number, time: number)
{
return (1-time)*start+time*end;
}
}
Per Request: SCSS
$diceSize: 55px;
.dice {
width: $diceSize;
height: $diceSize;
margin: 335px auto 0;
cursor: pointer;
box-shadow: 4px 5px 6px -1px rgba(0,0,0,0.94);
-webkit-box-shadow: 4px 5px 6px -1px rgba(0,0,0,0.94);
-moz-box-shadow: 4px 5px 6px -1px rgba(0,0,0,0.94);
}
.cube {
transform-style: preserve-3d;
width: 100%;
height: 100%;
position: relative;
animation-fill-mode: backwards;
}
.face {
display: flex;
position: absolute;
font-size: 2.5rem;
align-content: center;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background: rgb(255, 255, 255);
background-image: url('./../../../assets/DiceBackground.png');
background-size: auto 100%;
border: rgb(68, 68, 68) inset 1px;
pointer-events: none;
user-select: none;
}
.top {
transform: rotateX(90deg)
translateZ(calc($diceSize / 2));
}
.bottom {
transform: rotateX(-90deg)
translateZ(calc($diceSize / 2));
}
.front {
transform: rotateX(0deg)
translateZ(calc($diceSize / 2));
}
.back {
transform: rotateX(-180deg)
translateZ(calc($diceSize / 2));
}
I'm interested in how I can make a grid with an undetermined amount of columns and rows that I can put inside another div and have it not spill into others objects or mess with the parent size.
I want it to be square and I'm using Tailwind CSS but I can adapt to SCSS or vanilla CSS. Also I want it to be touchable/moveable with a mouse on desktop and touch capable devices.
How would I go about accomplishing this?
Assuming I've understood your question correctly, here is one way you could do it. I haven't tested it with a touch device but it shouldn't be hard to modify it to also respond to touch events.
const items = [
['a0', 'a1', 'a2'],
['b0', 'b1', 'b2'],
['c0', 'c1', 'c2']
];
let html = '';
for (let rowItems of items) {
html += '<div class="row">';
for (let item of rowItems) {
html += '<div class="item">';
html += item;
html += '</div>';
}
html += '</div>';
}
const viewElem = document.querySelector('#view');
const outputElem = document.querySelector('#output');
outputElem.innerHTML = html;
let mouseStartPos = null;
let startOffset = null;
outputElem.addEventListener('mousedown', e => {
outputElem.classList.remove('animate');
mouseStartPos = {
x: e.clientX,
y: e.clientY
};
startOffset = {
x: outputElem.offsetLeft - viewElem.offsetLeft,
y: outputElem.offsetTop - viewElem.offsetTop
};
});
window.addEventListener('mouseup', e => {
mouseStartPos = null;
startOffset = null;
outputElem.classList.add('animate');
const xGridOffset = -1 * Math.max(0, Math.min(Math.round((outputElem.offsetLeft - viewElem.offsetLeft) / -100), items.length - 1));
const yGridOffset = -1 * Math.max(0, Math.min(Math.round((outputElem.offsetTop - viewElem.offsetTop) / -100), items[0].length - 1));
outputElem.style.left = `${xGridOffset * 100}px`;
outputElem.style.top = `${yGridOffset * 100}px`;
});
window.addEventListener('mousemove', e => {
if (mouseStartPos) {
const xOffset = mouseStartPos.x - e.clientX;
const yOffset = mouseStartPos.y - e.clientY;
outputElem.style.left = `${-1 * xOffset + startOffset.x}px`;
outputElem.style.top = `${-1 * yOffset + startOffset.y}px`;
}
});
#view {
width: 100px;
height: 100px;
overflow: hidden;
border: 2px solid blue;
}
#output {
position: relative;
}
.row {
display: flex;
}
.item {
display: flex;
min-width: 100px;
width: 100px;
height: 100px;
box-sizing: border-box;
justify-content: center;
align-items: center;
border: 1px solid red;
}
#output.animate {
transition: left 1s ease 0s, top 1s ease 0s;
}
Drag it!<br/>
<br/>
<div id="view">
<div id="output"></div>
</div>
I need to make a ball grow until it reaches 400, which thereafter it would shrink the same way. Would really appreciate feedback what I'm doing wrong in my code:
var
ball1Size = 100
, ball2Size = 100
, ball2SizeStep = 50
;
function onBall2Click()
{
var ball2 = document.querySelector('.ball2');
ball2Size = ball2Size + 50;
if (ball2Size > 400) {
ball2Size = ball2Size - 100;
}
else {
ball2Size - 150;
}
ball2.innerText = ball2Size;
ball2.style.width = ball2Size;
ball2.style.height = ball2Size;
}
body {
background-color : black;
text-align : center;
}
h1 {
color : white;
}
div {
width : 100px;
height : 100px;
margin : auto;
margin-bottom : 10px;
border-radius : 50%;
transition : 0.3s;
line-height : 50px;
}
.ball2 {
background-color : orange;
}
<div class="ball2" onclick="onBall2Click()">
SIZE
</div>
See code fix and syntax fixes
here:
var ball1Size = 100;
var ball2Size = 100;
var ball2SizeStep = 50;
function onBall2Click() {
var ball2 = document.querySelector('.ball2');
ball2Size = ball2Size + ball2SizeStep;
if (ball2Size >= 400) {
ball2SizeStep = -50
} else if (ball2Size <= 50) {
ball2SizeStep = 50
}
ball2.innerText = ball2Size;
ball2.style.width = ball2Size + "px";
ball2.style.height = ball2Size + "px";
}
body {
background-color: black;
text-align: center;
}
h1 {
color: white;
}
div {
width: 100px;
height: 100px;
margin: auto;
margin-bottom: 10px;
border-radius: 50%;
transition: 0.3s;
line-height: 50px;
}
.ball2 {
background-color: orange;
}
<div class="ball2" onclick="onBall2Click()" >
SIZE
</div>
This is what you want to do, increase the balls size for each click that it can be increased for, i.e., is under 400, when it reaches 400, decrease the ball size for each click until it reaches its original size. Rinse and repeat.
var ball1Size = 100;
var ball2Size = 100;
var ball2SizeStep = 50;
let isBallDecreasing = false;
function onBall2Click() {
var ball2 = document.querySelector('.ball2');
if (ball2Size === 100) isBallDecreasing = false;
if (ball2Size >= 400 || isBallDecreasing) {
ball2Size -= 50;
isBallDecreasing = true;
} else {
ball2Size += 50
isBallDecreasing = false;
}
ball2.innerText = ball2Size;
ball2.style.width = ball2Size;
ball2.style.height = ball2Size
}
<div class="ball2" onclick="onBall2Click()">
SIZE
</div>
Using a boolean to track the direction can simplify it
let direction = 1;
const min = 100;
const max = 400;
const step = 50;
let current = 100;
var ball = document.querySelector(".ball2");
ball.addEventListener("click", function() {
current += direction * step;
if (current >= max) {
current = max;
direction = -1;
} else if (current <= min) {
current = min;
direction = 1;
}
updateElem();
});
function updateElem() {
ball.textContent = current;
ball.style.width = current + 'px';
ball.style.height = current + 'px';
}
updateElem();
body {
background-color: black;
text-align: center;
}
h1 {
color: white;
}
.ball {
width: 100px;
height: 100px;
margin: auto;
margin-bottom: 10px;
border-radius: 50%;
transition: 0.3s;
line-height: 50px;
}
.ball2 {
background-color: orange;
}
<div class="ball ball2">
SIZE
</div>
If you are going to have more than one of these you probably want to have some sort of way to keep track. Using data attributes can help out so you do not need more than one copy of the code.
document.querySelectorAll(".ball2").forEach(function (ball) {
ball.addEventListener("click", function () {
let direction = +ball.dataset.direction;
const min = +ball.dataset.min;
const max = +ball.dataset.max;
const step = +ball.dataset.step;
const current = +ball.dataset.current;
let updated = current + direction * step;
if (updated >= max) {
updated = max;
direction = -1;
} else if (updated <=min) {
updated = min;
direction = 1;
}
updateElem();
ball.dataset.direction = direction;
ball.dataset.current = updated;
});
function updateElem() {
const num = ball.dataset.current;
ball.textContent = num;
ball.style.width = num + 'px';
ball.style.height = num + 'px';
}
updateElem();
});
body {
background-color: black;
text-align: center;
}
h1 {
color: white;
}
.ball {
width: 100px;
height: 100px;
margin: auto;
margin-bottom: 10px;
border-radius: 50%;
transition: 0.3s;
line-height: 50px;
}
.ball2 {
background-color: orange;
}
<div
class="ball ball2"
data-direction="1"
data-min="100"
data-max="400"
data-step="50"
data-current="100">
SIZE
</div>
<div
class="ball ball2"
data-direction="-1"
data-min="100"
data-max="400"
data-step="50"
data-current="400">
SIZE
</div>
<div
class="ball ball2"
data-direction="1"
data-min="0"
data-max="1000"
data-step="100"
data-current="200">
SIZE
</div>
you can also Use CSS custom properties for simplifying your code:
const ball2 = document.querySelector('.ball2');
var
ballSz = 100
, ball2SizeStep = 50
;
ball2.onclick =()=>
{
ballSz += ball2SizeStep
ball2.textContent = ballSz
ball2.style.setProperty('--szHW', ballSz + 'px')
if (ballSz >= 400) ball2SizeStep = -50
if (ballSz <= 100) ball2SizeStep = +50
}
body {
background-color : black;
text-align : center;
}
h1 {
color : white;
}
div {
--szHW : 100px; /* <--- CSS custom properties */
width : var(--szHW);
height : var(--szHW);
margin : auto;
margin-bottom : 10px;
border-radius : 50%;
transition : 0.3s;
line-height : 50px;
}
.ball2 {
background-color : orange;
}
<div class="ball2">
SIZE
</div>
the same, for multiples balls
document.querySelectorAll('.ball2').forEach( ball2 =>
{
ball2.style.setProperty('--szHW', ball2.dataset.size + 'px') // init sizes according to data-size value
ball2.onclick =_=>
{
let
min = Number(ball2.dataset.min)
, max = Number(ball2.dataset.max)
, step = Number(ball2.dataset.step)
, size = Number(ball2.dataset.size)
;
if ( size === min && step < 0
|| size === max && step > 0 )
{
step *= -1
ball2.dataset.step = step
}
ball2.dataset.size = size += step
ball2.style.setProperty('--szHW', size + 'px')
}
})
body {
background-color : black;
text-align : center;
}
div.ball2 {
--szHW : 100px; /* <--- CSS custom properties */
width : var(--szHW);
height : var(--szHW);
margin : auto;
margin-bottom : 10px;
border-radius : 50%;
transition : 0.3s;
line-height : 50px;
background : orange;
}
div.ball2::before {
content: attr(data-size)
}
<div class="ball2" data-min="100" data-max="400" data-step="50" data-size="100"></div>
<div class="ball2" data-min="100" data-max="400" data-step="50" data-size="400"></div>
<div class="ball2" data-min="100" data-max="1000" data-step="100" data-size="200"></div>
I'm trying to generate a gradient for grayscale colors with the help of color.js library. The code below grabs the CSS variable --color and generates different tones by using the devalueByAmount() method. The gradient is successfully generated, however if possible I need help to generate smoother gradients.
Currently, the colors at the starting and ending points of the gradient seems too obvious. I'd like to smudge them out for smoother transition. Any ideas how I could achieve this? Any help would be much appreciated!
const root = document.documentElement;
const Color = net.brehaut.Color;
const dropBy = 0.2;
let numOfTones = 500;
let color = Color(getComputedStyle(document.documentElement).getPropertyValue('--color'));
let scheme = [...Array(numOfTones).keys()].map((i, _, a) => {
let drop = ((dropBy / numOfTones) * (i+1)).toFixed(3);
let tone = (i != 0) ? color.devalueByAmount(drop) : color;
return tone;
});
let grad = [...Array(numOfTones * 2).keys()].map((i, _, a) => {
let numOfSteps = a.length - 2;
let breakPoint = (((100 / 2) / (numOfSteps)) * (i-1)).toFixed(6);
let colorNo = (i > numOfTones) ? numOfTones - (i - numOfTones) : i;
let delimiter = i == (a.length - 1) ? ')' : ',';
let s = `${(i < 1) ? `repeating-linear-gradient(90deg,` : `${scheme[colorNo - 1]} ${breakPoint}%${delimiter}`}`;
return s;
}).join(' ');
root.style.setProperty('--grad', grad);
document.querySelector('div').classList.add('animate');
:root {
--color: #545454;
}
html {
height: 100vh;
}
body {
margin: 0;
padding: 0;
height: 100%;
}
div {
width: 100%;
height: 100%;
}
.animate {
background: var(--grad);
background-position: 400% 50%;
background-size: 400% 400%;
animation: gradient-animation 35s linear infinite;
}
#keyframes gradient-animation {
0% { background-position: 400% 50% }
100% { background-position: 0% 50% }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/color-js/1.0.1/color.min.js"></script>
<div></div>
I solved the issue by using a timing function to control the drop value. Don't know why I didn't think of this before, but thank you anyway.
-(Math.cos(Math.PI * x) - 1)
It now generates wider end points.
const root = document.documentElement;
const Color = net.brehaut.Color;
const dropBy = 0.2;
let numOfTones = 500;
let color = Color(getComputedStyle(document.documentElement).getPropertyValue('--color'));
let incrementer = 0;
let scheme = [...Array(numOfTones).keys()].map((i, _, a) => {
incrementer += 1 / numOfTones;
let drop = dropBy * -(Math.cos(Math.PI * incrementer) - 1) / 2;
let tone = (i != 0) ? color.devalueByAmount(drop) : color;
return tone;
});
let grad = [...Array(numOfTones * 2).keys()].map((i, _, a) => {
let numOfSteps = a.length - 2;
let breakPoint = (((100 / 2) / (numOfSteps)) * (i-1)).toFixed(6);
let colorNo = (i > numOfTones) ? numOfTones - (i - numOfTones) : i;
let delimiter = i == (a.length - 1) ? ')' : ',';
let s = `${(i < 1) ? `repeating-linear-gradient(90deg,` : `${scheme[colorNo - 1]} ${breakPoint}%${delimiter}`}`;
return s;
}).join(' ');
root.style.setProperty('--grad', grad);
document.querySelector('div').classList.add('animate');
:root {
--color: #545454;
}
html {
height: 100vh;
}
body {
margin: 0;
padding: 0;
height: 100%;
}
div {
width: 100%;
height: 100%;
}
.animate {
background: var(--grad);
background-position: 400% 50%;
background-size: 400% 400%;
animation: gradient-animation 35s linear infinite;
}
#keyframes gradient-animation {
0% { background-position: 400% 50% }
100% { background-position: 0% 50% }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/color-js/1.0.1/color.min.js"></script>
<div></div>
I got some simple bomberman game from code pen.For my study,i need to limit how many tiles and target.For tiles max 32 and target 7 (tiles grey & target red).
Here the source : codepen.io/Digiben/pen/oGYGrx
I dont understand how the script create the target and tiles with random algoritm.
Thanks for anyone who look this thread.
window.onload = function(){
//Map Kelas
class Map {
constructor (nbX, nbY, tileSize){
this.nbX = nbX;
this.nbY = nbY;
this.mapArray = new Array(this.nbX);
this.tileSize = tileSize;
this.map = document.getElementById('map');
}
init() {
console.log('Map size: ' + this.nbX * this.nbY);
let i = 0;
let j = 0;
let bool = null;
this.map.style.width = (this.tileSize * this.nbX) + 'px';
this.map.style.height = this.tileSize * this.nbY + 'px';
for (i = 0; i < this.nbX; i++) {
this.mapArray[i] = new Array(this.nbY);
for (j = 0; j < this.nbY; j++) {
bool = Math.random() >= 0.7 ? true : false;
if (bool) {
for (var z = Things.length - 1; i >= 0; i-) {
Things[i]
}
} else if (!bool) {
this.mapArray[i][j] = 1;
}
}
}
}
appendTile(i, j) {
let tile = document.createElement('div');
this.map.appendChild(tile);
tile.style.width = this.tileSize + 'px';
tile.style.height = this.tileSize + 'px';
tile.classList.add('tile');
tile.style.left = (i * this.tileSize) + 'px';
tile.style.top = (j * this.tileSize) + 'px';
}
getMapArray () {
return this.mapArray;
}
getMapSize () {
return {sizeX: this.nbX, sizeY:this.nbY}
}
}
//Create Target
class Target {
constructor (map, tileSize) {
this.mapArray = map.getMapArray();
this.playerSpace = map.getMapSize();
this.targetsArray = new Array();
this.possiblePositionToStartX = new Array();
this.possiblePositionToStartY = new Array();
this.tileSize = tileSize;
this.map = document.getElementById('map');
this.totalTargets = 0;
}
getTotalTargets(){
return this.totalTargets;
}
//Show Total Target
showTotalTargets () {
let totalDiv = document.querySelector('#score strong');
totalDiv.innerHTML = ' / ' + this.totalTargets;
}
showTargets(i, j) {
let tile = document.createElement('div');
this.map.appendChild(tile);
tile.classList.add('target');
tile.style.width = this.tileSize + 'px';
tile.style.height = this.tileSize + 'px';
// set attribute to identify the target when we need to remove it
tile.setAttribute('data-pos', i + ':' + j );
// positionning and styling
tile.style.left = (i * this.tileSize) + 'px';
tile.style.top = (j * this.tileSize) + 'px';
tile.style.backgroundColor = 'red';
tile.style.opacity = 0.5;
}
createTargets() {
//Target looping
for (var i = 1; i < this.playerSpace.sizeX-1; i++) {
//Maks Target 2D 10x10
this.targetsArray[i] = new Array();
if (i == 1) this.targetsArray[i-1] = new Array();
if (i == 8) this.targetsArray[i+1] = new Array();
for (var j = 1; j < this.playerSpace.sizeY-1; j++) {
this.targetsArray[i][j] = 1;
//Target aLgorithm
//Player dont Looping On red Zone
this.possiblePositionToStartX.push(i+1);
this.possiblePositionToStartY.push(j+1);
//Target Array if 0 to display Win on the End
this.targetsArray[i][j] = 0;
//Total Targets
this.totalTargets++;
//Show Target On map
this.showTargets(i, j);
}
}
}
//Show Total Targets
this.showTotalTargets();
}
// Start Player
getPossiblePosToStart() {
//Random Start PLayer
let xPos = this.possiblePositionToStartX[Math.floor(Math.random() * (this.possiblePositionToStartX.length))];
let yPos = this.possiblePositionToStartY[Math.floor(Math.random() * (this.possiblePositionToStartY.length))];
return {x: xPos, y: yPos}
}
//Player Array
getTargetsArray(){
return this.targetsArray;
}
}
//PLayer CLass
class Player {
constructor (mapArray, map, targets, tileSize) {
this.positionArray = mapArray;
this.position = {x: 0, y: 0}
this.playerDiv = document.getElementById('player');
this.playerDiv.style.left = 0;
this.playerDiv.style.top = 0;
this.playerDiv.style.right = 0;
this.playerDiv.style.bottom = 0;
this.playerDiv.style.width = tileSize + 'px';
this.playerDiv.style.height = tileSize + 'px';
this.playerSpace = map.getMapSize();
this.playerMap = map.getMapArray();
this.score = 0;
this.targetsArray = targets.getTargetsArray();
this.totalTargets = targets.getTotalTargets();
this.tileSize = tileSize;
}
//Record Posisition Player
recordPosition(mapArray){
this.positionArray = mapArray;
}
//Reset Score when Restart The game
static resetScore() {
let scoreSpan = document.querySelector('#score span'); scoreSpan.innerHTML = '0';
}
//Set Palyer
setPosition (position){
this.playerDiv.style.left = (position.x * this.tileSize) + 'px';
this.playerDiv.style.top = (position.y * this.tileSize) + 'px';
this.position.x = position.x;
this.position.y = position.y;
}
getPosition() {
return this.position;
}
//Limt Map
moveRight() {
if(this.position.x > this.playerSpace.sizeX-2) return false;
if(this.positionArray[this.position.x+1][this.position.y] != 0){
this.position.x++;
let nb = this.playerDiv.style.left.split('px');
this.playerDiv.style.left = (parseInt(nb[0]) + this.tileSize) + 'px';
console.log('Droite | X : ' + this.playerDiv.style.left);
console.log(this.position.x + ' : ' + this.position.y);
} else {
console.log('Not OK');
}
}
moveDown() {
if(this.position.y > this.playerSpace.sizeY-2) return false;
if(this.positionArray[this.position.x][this.position.y+1] != 0){
this.position.y++;
let nb = this.playerDiv.style.top.split('px');
this.playerDiv.style.top = (parseInt(nb[0]) + this.tileSize) + 'px';
console.log('Bas | Y : ' + this.playerDiv.style.top);
console.log(this.position.x + ' : ' + this.position.y);
} else {
console.log('Not OK');
}
}
moveLeft() {
if(this.position.x == 0) return false;
if(this.positionArray[this.position.x-1][this.position.y] != 0){
this.position.x--;
let nb = this.playerDiv.style.left.split('px');
this.playerDiv.style.left = (parseInt(nb[0]) - this.tileSize) + 'px';
console.log('Gauche | X : ' + this.playerDiv.style.left);
console.log(this.position.x + ' : ' + this.position.y);
} else {
console.log('Not OK');
}
}
moveUp() {
if(this.position.y <= 0) return false;
if(this.positionArray[this.position.x][this.position.y-1] != 0){
this.position.y--;
let nb = this.playerDiv.style.top.split('px');
this.playerDiv.style.top = (parseInt(nb[0]) - this.tileSize) + 'px';
console.log('Haut | Y : ' + this.playerDiv.style.top);
console.log(this.position.x + ' : ' + this.position.y);
} else {
console.log('Not OK');
}
}
//Update Score
updateScore () {
let scoreDiv = document.querySelector('#score span');
scoreDiv.innerHTML = this.score;
//Winner Message
if(this.score == this.totalTargets) document.querySelector ('#win').classList.add('show');
console.log('Score : ' + this.score);
}
//Update Target Array
updateTargetsArray (posx, posy){
this.targetsArray[posx][posy] = 1;
console.log('Array state : ');
console.log(this.targetsArray);
}
//Remove Target
removeTarget(posx, posy) {
let targetToRemove = document.querySelectorAll('.target');
let coords = posx + ':' + posy;
let attr = '';
//Loop To find Right Target accroding Coordinates Player
for (var i = 0; i< targetToRemove.length; i++) {
attr = targetToRemove[i].getAttribute('data-pos');
if(attr == coords) {
targetToRemove[i].remove();
//Update Score
this.score++;
this.updateScore();
}
}
//Remove Html node (Remove Array Target)
if(this.targetsArray[posx][posy] == 0){
this.targetsArray[posx][posy] == 1;
}
}
//Plant Bomb
plantBomb(){
//Make Child Bomb
let map = document.getElementById('map');
let bomb = document.createElement('div');
map.appendChild(bomb);
bomb.style.width = this.tileSize + 'px';
bomb.style.height = this.tileSize + 'px';
//Posision Bomb
bomb.classList.add('bomb');
bomb.style.left = (this.position.x * this.tileSize) + 'px';
bomb.style.top = (this.position.y * this.tileSize) + 'px';
//Variables
var posx = this.position.x;
var posy = this.position.y;
var that = this;
var timer = setInterval(bombTimer, 500, posx, posy, that);
var iter = 0;
//BombTimer
function bombTimer() {
switch (iter) {
case 1:
bomb.classList.add('waiting');
break;
case 2:
bomb.classList.add('before');
bomb.classList.remove('waiting');
break;
case 3:
bomb.classList.add('explode');
bomb.classList.remove('before');
break;
case 4:
clearInterval(timer);
bomb.remove();
that.updateTargetsArray(posx, posy);
that.removeTarget(posx, posy);
default:
break;
}
iter++;
}
}
}
//Game Class
class Game {
constructor (tileSize, mapX, mapY) {
//Create Map
var map = new Map(mapX,mapY, tileSize);
map.init();
//Create Target
var targets = new Target(map, tileSize);
targets.createTargets();
//Create PLayer
var player = new Player(map.getMapArray(), map, targets, tileSize);
//Place The player
player.setPosition(targets.getPossiblePosToStart());
//Keyboard Events
document.onkeydown = checkKey;
function checkKey(e) {
e = e || window.event;
if (e.keyCode == '38') {
player.moveUp();
}
else if (e.keyCode == '40') {
player.moveDown();
}
else if (e.keyCode == '37') {
player.moveLeft();
}
else if (e.keyCode == '39') {
player.moveRight();
}
else if (e.keyCode == '32') {
player.plantBomb();
}
}
}
//Destroy Game
static destroy () {
let targets = document.querySelectorAll('.target');
let tiles = document.querySelectorAll('.tile');
Player.resetScore();
if(tiles){
targets.forEach(function(element) {
element.remove();
});
tiles.forEach(function(element) {
element.remove();
});
}
}
}
class Session {
constructor () {
this.totalTargets = 0;
this.players = {};
this.restartBtn = document.querySelector('#restart');
this.restartBtn.addEventListener('click', function() {
Session.restart();
});
}
static restart () {
Game.destroy();
var game = new Game(25, 10, 10);
}
}
var session = new Session();
};
#map {
width: 500px;
height: 500px;
background: lightgrey;
position: relative;
margin: auto;
}
#game {
width: 500px;
height: 500px;
position: relative;
margin: auto;
}
#map .tile {
width: 50px;
height: 50px;
background: grey;
position: absolute;
outline: 1px solid #eee;
}
#map .target {
width: 50px;
height: 50px;
background: red;
position: absolute;
outline: 1px solid #eee;
}
#map #player {
border-radius: 25%;
width: 50px;
height: 50px;
position: absolute;
background: #222222;
z-index: 1;
transition: 0.1s;
}
.bomb {
border-radius: 100%;
width: 50px;
height: 50px;
position: absolute;
background: #333;
z-index: 1;
transition: 0.3s ease;
}
.bomb.waiting {
animation: waiting 2s infinite;
}
.bomb.before {
animation: before 1s infinite;
}
.bomb.explode {
animation: explode 1s infinite;
}
#score p, #score span {
font-family: sans-serif;
color: #333;
font-size: 16px;
display: inline;
}
#keyframes waiting {
0% {
transform: scale(0.9);
}
100% {
transform: scale(1.1);
}
}
#keyframes before {
0% {
transform: scale(1.0);
background: orange;
}
100% {
transform: scale(1.2);
background: red;
}
}
#keyframes explode {
0% {
transform: scale(1.0);
background: red;
opacity: 1;
}
100% {
transform: scale(2);
background: yellow;
opacity: 0;
}
}
#keyframes win {
0% {
opacity: 0;
}
10% {
opacity: 1;
}
66% {
opacity: 1;
}
100% {
opacity: 0;
}
}
h4 {
font-family: sans-serif;
color: #333;
text-align: center;
}
p, strong {
font-family: sans-serif;
color: #333;
text-align: left;
font-size: 12px;
}
#win {
position: fixed;
left: 0;
right: 0;
top:0;
bottom: 0;
z-index: 9999999;
background: rgba(181, 181, 195, 0.1);
pointer-events: none;
opacity: 0;
}
#win p {
color: red;
font-size: 130px;
text-align: center;
font-family: sans-serif;
text-transform: uppercase;
height: 100%;
margin: 0;
top: 50%;
position: absolute;
left: 50%;
transform: translate(-50%, -25%);
right: 0;
bottom: 0;
font-weight: bold;
text-shadow: 5px 5px #333;
}
#win.show {
animation: win 4s ease;
}
#restart {
text-align: center;
padding: 10px 20px;
font-family: sans-serif;
color: #333;
outline: #ccc 1px solid;
display: table;
margin: auto;
margin-top: 20px;
cursor: pointer;
transition: 0.1s ease;
}
#restart:hover {
background: #eee;
}
<!DOCTYPE html>
<html>
<head>
<title>Bomberman</title>
<link href="bomber.css" type="text/css" rel="stylesheet">
<script type="text/javascript" src="1.js"></script>
</head>
<body>
<h4>Space bar to plant a bomb / Arrows to move </h4>
<div id="win"><p>WIN !</p></div>
<div id="restart"> RESTART </div>
<div id="score"><p>Score: </p><span>0</span><strong> / 0</strong></div>
<section id="game">
<div id="map">
<div id="player"></div>
</div>
</section>
</body>
</html>
I'm not certain I understand your question, but I think you're trying to debug the createTargets method in the Targets class. The problem there is an extra closing bracket (}) right before the line with //Show Total Targets.