How do i add half hexagons on either side of my screen? - javascript

I have a website with HTML, CSS, and JS.In which JavaScript determines the browser's dimensions and then adds the hexagon-div to a container to create the Honeycomb Hexagon Grid over the entire page. I discovered that there are empty spots in the corners where a whole hexagon cannot fit, and I don't want that.
Half hexagons should be placed there to fill the voids.
Furthermore, I have svg code for half hexagons:
HALF LEFT HEXAGON:
<svg viewBox="0 0 50 116" width="50" height="116">
<polygon points="50,0 50,115.48 0,86.61 0,28.87" fill="#28282B"/>
</svg>
HALF RIGHT HEXAGON:
<svg viewBox="50 0 50 116" width="50" height="116">
<polygon points="50,0 100,28.87 100,86.61 50,115.48" fill="#28282B"/>
</svg>
const hexagonWidth = 100;
const hexagonHeight = 115.48;
const rowHeight = 125;
const numRows = 12;
const numCols = 19;
const container = document.getElementById("hexagons");
for (let i = 0; i < numRows; i++) {
const row = document.createElement("div");
row.classList.add("row");
if (i % 2 !== 0) {
row.classList.add("row-moved");
}
for (let j = 0; j < numCols; j++) {
const hexagon = document.createElement("div");
hexagon.classList.add("hexagon");
hexagon.style.width = hexagonWidth + "px";
hexagon.style.height = hexagonHeight + "px";
hexagon.style.margin = "5.5px 2px";
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("height", rowHeight);
const polygon1 = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
polygon1.setAttribute("points", "50,0 100,28.87 100,86.61 50,115.48 0,86.61 0,28.87");
polygon1.setAttribute("fill", "#28282B");
const polygon2 = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
polygon2.setAttribute("points", "50,0 100,28.87 100,86.61 50,115.48");
polygon2.setAttribute("fill", "#28282B");
svg.appendChild(polygon1);
svg.appendChild(polygon2);
hexagon.appendChild(svg);
row.appendChild(hexagon);
}
container.appendChild(row);
}
body {
background: #171717;
overflow: hidden;
}
.row {
width: 100vw;
height: 125px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
margin-top: -35px;
margin-left: -33px;
}
.row .hexagon {
position: relative;
width: 100px;
height: 115.48px;
margin: 5.5px 2px;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.2s linear;
}
.row .hexagon:hover polygon {
fill: #3fff3c;
}
.row.row-moved {
margin-left: 19px;
}
.row .hexagon svg polygon {
transition: all 0.2s linear;
}
.row .hexagon svg polygon:hover {
fill: #3fff3c;
}
<div id="hexagons"></div>

Related

How to create infinite loop slider using offset in vanilla JS?

So I am working on this project and I made a slider that slides 3 pics at a time but when it comes to the last 3 pics it stops (because of the condition) but I want to make it into infinite loop and I want it to autoslide every 3 seconds. Does anybody know how I can rewrite this part of code to make this work?
const carousel = document.querySelector(".carouselSlides");
const card = carousel.querySelector(".card");
const leftButton = document.querySelector(".slideLeft");
const rightButton = document.querySelector(".slideRight");
const carouselWidth = carousel.offsetWidth;
const cardStyle = card.currentStyle || window.getComputedStyle(card);
const cardMarginRight = Number(cardStyle.marginRight.match(/\d+/g)[0]);
const cardCount = carousel.querySelectorAll(".card").length;
let offset = 0;
const maxX = -(
(cardCount / 3) * carouselWidth +
cardMarginRight * (cardCount / 3) -
carouselWidth -
cardMarginRight
);
leftButton.addEventListener("click", function() {
if (offset !== 0) {
offset += carouselWidth + cardMarginRight;
carousel.style.transform = `translateX(${offset}px)`;
}
});
rightButton.addEventListener("click", function() {
if (offset !== maxX) {
offset -= carouselWidth + cardMarginRight;
carousel.style.transform = `translateX(${offset}px)`;
}
});
.carouselContainer {
display: flex;
align-items: center;
justify-content: space-around;
position: relative;
overflow: hidden;
height: 58vh;
width: 92vw;
margin: 0 auto;
}
.carouselSlides {
display: flex;
margin: 0;
padding: 0;
list-style: none;
width: 100%;
position: absolute;
left: 0;
transition: all 1s ease;
}
.button-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
}
.slideLeft {
position: relative;
color: #f1f1f1;
background-color: transparent;
border: none;
font-size: 4rem;
right: 26rem;
}
.slideRight {
position: relative;
color: #f1f1f1;
background-color: transparent;
border: none;
font-size: 4rem;
left: 26rem;
}
<div class="carouselContainer">
<div class="carouselSlides">
<div id="slide0" class="card">
<div class="card__thumbnail">
<span class="card__title">FADE</span>
<button class="morebtn" onclick="">READ MORE</button>
</div>
</div>
</div>
<div class="button-wrapper">
<button class="slideLeft">‹</button>
<button class="slideRight">›</button>
</div>
</div>

Make both overlapping divs clickable?

Is it possible to make two overlapping divs, both clickable?
I've appended divs to two containers, #container and #container2. Their styles are exactly the same only except one is flex-direction: column; and one is flex-direction: column;. Both position:absolute with #container2 on top. I made each of the appended child clickable to fill its background color. Only the div on top is clickable so far, is there a way to make both clickable? or is there another way to have the bottom div react to my clicks?
window.addEventListener('load', init);
function init() {
calculateGrid();
//calculate grid
function calculateGrid() {
var w = window.innerWidth;
var h = window.innerHeight;
var totalNum = Math.trunc(w / 25) * Math.trunc(h / 25);
function randomInRange(from, to) {
let x = Math.random() * (to - from);
return x + from;
};
for (var i = 0; i < totalNum; i++) {
var div = document.createElement('div');
div.setAttribute('class', 'grid');
div.style.width = randomInRange(3, 10) + 'vw';
div.style.height = randomInRange(5, 10) + 'vh';
document.getElementById('container').appendChild(div);
document.getElementById('container2').appendChild(div.cloneNode(true));
}
};
$(".grid").click(function() {
$(this).toggleClass('selected');
});
};
#container {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
flex-direction: column;
overflow: hidden;
}
#container .grid {
border: 1px solid blue;
}
#container2 {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
flex-direction: row;
overflow: hidden;
}
#container2 .grid {
border: 1px solid red;
}
.grid {
font-size: 10px;
color: white;
}
#container .selected {
background-color: blue;
}
#container2 .selected {
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div id="wrapper">
<div id="container"></div>
<div id="container2"></div>
</div>
View on CodePen
One method is to use Document.elementsFromPoint() to return "an array of all elements at the specified coordinates". Iterate through that array, adding the "selected" class to "grid" elements.
window.addEventListener('load', init);
function init() {
// build grid
function calculateGrid() {
var w = window.innerWidth;
var h = window.innerHeight;
var totalNum = Math.trunc(w / 25) * Math.trunc(h / 25);
function randomInRange(from, to) {
let x = Math.random() * (to - from);
return x + from;
};
for (var i = 0; i < totalNum; i++) {
var div = document.createElement('div');
div.setAttribute('class', 'grid');
div.style.width = randomInRange(3, 10) + 'vw';
div.style.height = randomInRange(5, 10) + 'vh';
document.getElementById('container1').appendChild(div);
document.getElementById('container2').appendChild(div.cloneNode(true));
}
};
// handle grid clicks
function handleGridClick(e) {
let elms = document.elementsFromPoint(e.clientX, e.clientY);
Array.from(elms).forEach(elm => {
if (elm.classList.contains('grid'))
elm.classList.add('selected');
});
}
// initialize grid and click handler
calculateGrid();
document.addEventListener('click', handleGridClick);
};
.container {
width: 100vw;
height: 95vh;
position: absolute;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
overflow: hidden;
}
#container1 {
flex-direction: column;
}
#container1 .grid {
border: 1px solid blue;
}
#container1 .grid.selected {
background-color: blue;
}
#container2 .grid {
border: 1px solid red;
}
#container2 .grid.selected {
background-color: red;
}
<div id="wrapper">
<div id="container1" class="container"></div>
<div id="container2" class="container"></div>
</div>
You can't actually hover two items at the same time in plain 'ol HTML/CSS - for that you will need JavaScript as explained in the accepted solution. However, there's a CSS-only solution to allow hovering over the different layers, which was fun to figure out at the very least.
So the idea is that you have these invisible boxes on top of the visible ones. The invisible boxes only have borders such that any time your mouse hits a border, some clever z-index swapping takes place to make the visible containers change their stacking order.
For every .grid item you need to create a corresponding .box item: https://jsfiddle.net/ryanwheale/01v5yz86/93/

Strike effect in Tic tac toe game

I was working on a Tic Tac Toe game and was trying to make a strike effect for the Winner's squares.
My idea was, I could take first and last squash, get their mid position and use a canvas to create a line but its not working properly.
Following is a sample code with Fiddle link:
function TicTacToe(container) {
let count = 0;
const getLabel = () => count++ % 2 === 0 ? 'X' : 'Y';
function createGrid() {
const handlerFn = function() {
this.innerText = getLabel();
this.removeEventListener('click', handlerFn);
}
Array.from({
length: 9
}, (_, i) => {
const div = document.createElement('div');
div.classList.add('tile')
div.addEventListener('click', handlerFn)
container.append(div);
});
}
function createStrikeLine() {
const tiles = document.querySelectorAll('.tile');
const [ startX, startY ] = getPosition(tiles[0]);
const [ endX, endY ] = getPosition(tiles[8]);
console.log(startX, startY, endX, endY)
const canvas = document.getElementById('ctx-strike');
const context = canvas.getContext('2d');
context.beginPath();
context.moveTo(startX, startY);
context.lineTo(endX, endY);
context.stroke();
context.closePath();
}
function getPosition(element) {
const left = element.offsetLeft;
const top = element.offsetTop;
const height = Math.floor(element.offsetWidth / 2);
return [ left + height, top + height ];
}
createGrid();
createStrikeLine();
}
const containerDiv = document.querySelector('.content');
TicTacToe(containerDiv)
div {
display: flex;
}
.container {
align-items: center;
justify-content: center;
height: 95vh;
width: 95vw;
}
.content {
flex-direction: row;
flex-wrap: wrap;
width: 30vmax;
}
#ctx-strike {
/* position: absolute; */
height: 30vmax;
border: 1px solid black;
}
.tile {
margin: 2px;
background: white;
border: 2px solid gray;
border-radius: 4px;
width: 8vw;
height: 8vw;
align-items: center;
justify-content: center;
font-size: 2em;
}
.strike-through {
position: absolute;
z-index: 1;
border-bottom: 4px solid red;
height: 6vh;
width: 21vmax;
}
.translate-45 {
-webkit-transform: rotate(45deg);
}
<div class="container">
<div class="content"></div>
<canvas id='ctx-strike'></canvas>
</div>
Now I understand, issue is with co-ordinates, but I tried to make the canvas full with, still it fails. So the question,
How to determine correct co-ordinates for any tile?
Is there a better way to make stike effect other than canvas?
Another issue i saw was, entire UI is responsive but not the canvas. If you resize container, tiles expand/shrink but the line remains same
Post the last update:
Another issue i saw was, entire UI is responsive but not the canvas. If you resize container, tiles expand/shrink but the line remains same
I thought of using div and css instead of canvas. Yes, canvas would have been less work, css is able to handle responsiveness a bit.
Idea:
I created 8 classes with possible patterns for strike through:
Row-wise: Players can win in 3 pattern if they choose same value in either of rows.
Column-wise: Players can win in 3 pattern if they choose same value in either of column.
Diagonals: They can choose in either diagonal way.
Now in my validation logic, all I have to do is decide if there is a win, choose win pattern and pass it to my function.
Sample Code
Note: I added dropdown so users can play and check these patterns individually.
function TicTacToe(container) {
const strikePatterns = [
'row-1', 'row-2', 'row-3',
'col-1', 'col-2', 'col-3',
'dig-1', 'dig-2'
];
let count = 0;
const getLabel = () => count++ % 2 === 0 ? 'X' : 'Y';
function createGrid() {
const handlerFn = function() {
this.innerText = getLabel();
this.removeEventListener('click', handlerFn);
}
Array.from({
length: 9
}, (_, i) => {
const div = document.createElement('div');
div.classList.add('tile')
div.addEventListener('click', handlerFn)
container.append(div);
});
}
function createStrikeLine(patternName) {
const div = document.createElement('div');
div.classList.add('strike-through');
div.classList.add(patternName)
container.append(div);
}
createGrid();
function createPatternSelect(value) {
const select = document.createElement('select');
strikePatterns.forEach(function(pattern) {
const option = document.createElement('option');
option.innerText = pattern;
option.value = pattern;
select.append(option);
})
if (value) {
select.value = value;
}
select.addEventListener('change', function(event) {
container.innerHTML = '';
createGrid();
createPatternSelect(this.value);
createStrikeLine(this.value);
})
container.append(select)
}
createPatternSelect();
}
const containerDiv = document.querySelector('.content');
TicTacToe(containerDiv)
div {
display: flex;
}
.container {
align-items: center;
justify-content: center;
height: 95vh;
width: 95vw;
}
.content {
flex-direction: row;
flex-wrap: wrap;
width: 30vmax;
align-items: center;
justify-content: center;
}
#ctx-strike {
position: absolute;
border: 1px solid black;
}
.tile {
margin: 2px;
background: white;
border: 2px solid gray;
border-radius: 4px;
width: 8vw;
height: 8vw;
align-items: center;
justify-content: center;
font-size: 2em;
}
.strike-through {
border-bottom: 2px solid red;
position: absolute;
width: 18vw;
height: 1px;
}
.row-1 {
margin-top: -10vw;
}
.row-2 {
margin-top: 0;
}
.row-3 {
margin-top: 8vw;
}
.col-1 {
margin-left: -9vw;
transform: rotate(90deg);
}
.col-2 {
margin-left: 0;
transform: rotate(90deg);
}
.col-3 {
margin-left: 9vw;
transform: rotate(90deg);
}
.dig-1 {
margin-top: -1vw;
width: 27vw;
transform: rotate(-45deg);
}
.dig-2 {
margin-top: -1vw;
width: 27vw;
transform: rotate(45deg);
}
<div class="container">
<div class="content"></div>
</div>

Change transition duration during loop with requestAnimationFrame

I'm trying to change the transition duration in-between the steps of a setTimeout loop.
When the button centre is clicked, setView changes the position of content so that the inner div remains in the centre of the screen. It uses a forEach loop to change the centring using an array of inner divs.
I'd like the transition between the change from alpha inner to bravo inner to take 1s, but once it gets there the transition should be 0s so that is no lag with the centring.
Here's a codepen, then built in is getting an error.
I'm using vanilla ES6.
const content = document.querySelector('.content');
let frame;
function getBounds(div) {
const element = document.getElementById(div);
const body = element.children[0];
const {
x, y, width, height,
} = body.getBoundingClientRect();
return box = {
element, body, x: Math.round(x), y: Math.round(y), width, height,
};
}
function setView(div) {
const centerW = window.innerWidth / 2;
const centerH = window.innerHeight / 2;
const { left, top } = content.getBoundingClientRect();
content.style.left = `${(centerW + left) - (div.x + div.width / 2)}px`;
content.style.top = `${(centerH + top) - (div.y + div.height / 2)}px`;
}
function centreDiv(string) {
const outers = string.split(' ');
outers.forEach(function(outer, index) {
setTimeout(function() {
content.style.transition = 'all 0s linear'; // this works
if (frame) {
cancelAnimationFrame(frame);
}
function tick(now) {
setView(getBounds(outer));
frame = requestAnimationFrame((timestamp) => tick(timestamp, outer));
}
frame = requestAnimationFrame((timestamp) => tick(timestamp, outer));
},
5000 * index);
content.style.transition = 'all 1s linear'; // this isn't working
});
}
document.getElementById('centre').onclick = () => centreDiv(centreText.value.toLowerCase());
* {box-sizing: border-box;}
html,body,div,span {padding: 0; margin: 0; border: 0;}
.controls {
position: absolute;
z-index: 1000;
padding-top: 10px;
padding-left: 20px;
font-family: sans-serif;
color: white;
}
.content {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height:100vh;
background-color: rgb(138,141,143);
transition: all 0s linear;
}
.centre {
position: absolute;
display: block;
width: 5px;
height: 5px;
background-color: white;
}
.outer {
position: absolute;
display: flex;
align-items: center;
justify-content: flex-end;
}
.inner {
display: block;
width: 30px;
height: 30px;
background-color: rgb(37,40,42);
}
.animate {
animation: spin infinite linear;
}
#alpha {
width: 80%;
height: 80%;
background-color: rgb(255,205,0);
animation-duration: 10s;
}
#bravo {
width: 30%;
height: 30%;
background-color: rgb(242,169,0);
animation-duration: 5s;
}
#keyframes spin {
100% {
transform: rotateZ(360deg);
}
}
<div class="controls">
centre: <input id="centreText" type="text" value="alpha bravo"/><button type="button" id="centre">centre</button>
</div>
<div class="content">
<div id="alpha" class="animate outer">
<div class="inner"></div>
</div>
<div id="bravo" class="animate outer">
<div class="inner"></div>
</div>
<div class="centre"></div>
</div>

splitting div into multiple divs in javascript

Hi I am trying to make the columns and rows in the mainContent div but the problem is the it is getting out of the mainContent after 2-3 clicks. I want it to remain inside and should create equally sized columns and rows inside of it. here is my code.
var test2 = document.getElementById('btn');
test2.addEventListener('click', function() {
console.log('clicked');
var contain = document.getElementById('contentArea'); // for selecting id
var newGriding = document.createElement('div');
newGriding.setAttribute('id', 'grid');
contain.appendChild(newGriding);
});
#contentArea {
background-color: #babab3;
height: 74vh;
margin: 0 auto;
}
#grid {
margin: 0;
padding: 0;
border: none;
outline: 5px dashed #aba4a4;
display: inline-block;
height: 100%;
width: 50%;
}
<div id="contentArea">
</div>
<button id="btn">
create
</button>
Because you are using fixed height for the appending element.
You should resize the element after every click using some logic or you can use the display of your parent as flex and flex wrap true.
var test2 = document.getElementById('btn');
test2.addEventListener('click', function() {
var contain = document.getElementById('contentArea'); // for selecting id
var newGriding = document.createElement('div');
newGriding.setAttribute('id', 'grid');
contain.appendChild(newGriding);
});
#contentArea {
background-color: #babab3;
height: 74vh;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
}
#grid {
margin: 0;
padding: 0;
border: none;
outline: 5px dashed #aba4a4;
display: inline-block;
width: 50%;
}
<div id="contentArea">
</div>
<button id="btn">create</button>
or
var test2 = document.getElementById('btn');
test2.addEventListener('click', function() {
var contain = document.getElementById('contentArea'); // for selecting id
var newGriding = document.createElement('div');
newGriding.setAttribute('id', 'grid');
contain.appendChild(newGriding);
resizeDiv();
});
var maxInRow = 2;
function resizeDiv() {
var allGrids = document.querySelectorAll("#contentArea > #grid");
var width = 100 / maxInRow;
var len = allGrids.length;
var colNo = Math.floor(len / maxInRow);
colNo = colNo - (len / maxInRow) == 0 ? colNo : colNo + 1;
var height = 100 / colNo;
for (var i = 0; i < len; i++) {
allGrids[i].style.width = width + "%";
//"calc(" + width + "% - 10px)"; --- if doesn't want box-sizing to be borderbox
allGrids[i].style.height = height + "%";
//"calc(" + height + "% - 10px)"; --- if doesn't want box-sizing to be borderbox
//reduce the size of box which increased due to outline
}
}
#contentArea {
background-color: #babab3;
height: 74vh;
margin: 0 auto;
position: relative;
}
#grid {
margin: 0;
padding: 0;
border: 5px dashed #aba4a4;
display: inline-block;
height: 100%;
width: 50%;
position: relative;
box-sizing: border-box;
}
<div id="contentArea">
</div>
<button id="btn">
create
</button>

Categories