Rainbow Slinky
Tags: html, javascript, css
Published: 2021-07-16 20:43:26

<img width="100%" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wfifb0zohnho19etcv2s.gif" />

So I have seen lots of posts about people sharing fun little projects they have made so I would like to share my Rainbow slinky with you guys. Please go easy on me. This is my first post so I'm not entirely sure what I'm doing but anyways, lets get into it!😁

The way this will work is by creating a bunch of empty div elements. On the `mousemove` event we will pull the X and Y coordinates of the mouse and then incrementally update the `div's` to move to that location on the screen creating a really cool slinky effect.

##HTML
To begin we will need create our markup. This will be very simple. It will consist of 2 `div's`, 1 that we will use as our cursor because we will hide the default mouse cursor and then another `div` to contain all the rings of our slinky.
```html
<div id="ring_container"></div>
<div class="dot mouse_el"></div>
```

##CSS
We can now add our `CSS`. This will remove the cursor from the page, give our rings a default style and also style the dot which will become our new cursor
```css
* {
    cursor: none !important;
}
html,
body {
    height: 100vh;
    overflow: hidden;
    background-color: #000;
}      
.mouse_el {
    border-radius: 50%;
    position: absolute;
    top: 50%;
    left: 50%;
}
.ring {
    border: 5px solid #fff;
}
.dot {
    height: 10px;
    width: 10px;
    background-color: #fff;
}
```

##Javascript
Now that our `HTML` and `CSS` is pretty much done we can get onto the logic behind the slinky. First we need to define some global variables. The `rings` variable will store our collection of rings so we can use it in multiple functions. The `dot` is our cursor and the `colors` will be the colors we loop through when creating our `div's`, in this case the colors of the rainbow but these can be changed.
```javascript
let rings;
let dot = document.querySelector('.dot');
let colors = ['#9400D3', '#4B0082', '#0000FF', '#00FF00', '#FFFF00', '#FF7F00', '#FF0000'];
```

We now need to create the `div's` and append them to the rings container. Once all the rings are created we will then set the colors of the `div's`
```javascript
function createRings(numRings) {
    if (numRings > 0) {
        for (let i = 0; i <= numRings; i++) {
            const div = document.createElement('div');
            div.classList.add('mouse_el', 'ring');
            document.getElementById('ring_container').appendChild(div);
        }
        setColors();
    }

}
function setColors() {
    let index = 0;
    const dimensions = 60;
    rings = document.querySelectorAll('.ring');

    rings.forEach((el) => {
        index = index < colors.length ? index : 0;
        el.style.borderColor = colors[index];
        el.style.width = dimensions + 'px';
        el.style.height = dimensions + 'px';
        index++;
    })
}
```

Now we need to create the function that will update the X and Y coordinates of all the `div's`. Here we will need to get the size of the border because we will need to take this into account when trying to calculate the center of the rings for the cursor.
```javascript
function updateCursor(eventObj) {
    let lag = 50;
    const x = eventObj.clientX;
    const y = eventObj.clientY;
    let ringBorderSize = parseInt(window.getComputedStyle(rings[0]).getPropertyValue('border-top-width'));

    calcPosition(dot, x, y, ringBorderSize);

    rings.forEach((el, i) => {
        lag += 50;
        setTimeout(() => { calcPosition(el, x, y) }, lag + 50);
    })
}
```

Now its time for some quick math. We need to check if the `ringBorderSize > 0` because then we know that we are trying to calculate the position for the cursor in which case we will need to take the size of the borders into account or else the dot won't sit perfectly in the center of the rings.
```javascript
function calcPosition(el, x, y, border_size = 0) {
    if (border_size <= 0) {
        el.style.left = x - (el.clientWidth / 2) + 'px';
        el.style.top = y - (el.clientHeight / 2) + 'px';
    } else {
        el.style.left = x - ((el.clientWidth / 2) - border_size) + 'px';
        el.style.top = y - ((el.clientHeight / 2) - border_size) + 'px';
    }
}
```

The last step is to setup the `eventListener` and call the `createRings` function. If you find it lagging a bit, try reduce the number passed to create rings and see what works best for you
```javascript
window.addEventListener('mousemove', (e) => { updateCursor(e) });

createRings(75);
```

I hope you guys have as much fun with this as I've had!🥳