简体   繁体   中英

Windows-like Tile Border Effect

So Windows has this incredible effect when you place your mouse near a 'tile' like so:

  1. When you place your mouse outside a tile, but within the tile container (here it's at the intersection of the 4 tiles):

在此处输入图像描述

  1. When you place your mouse inside a tile (here it's near the top right of the mobile hotspot tile):

在此处输入图像描述

  1. When you place your mouse outside a tile but at the mid point of some length of the tile (notice how the opposite sides of the tile light up a bit):

在此处输入图像描述

Just for curiosty/fun, what is the best way to emulate such an effect using css and/or JS? I'm using a grid layout to create the tiles, but since I'd rather solve the problem than get into specifics, let's simplify things with these assumptions:

a) The positions of the tiles are fixed and won't change during while the user is on the page
b) It is possible to get the row and column number of any tile using JS
c) All tiles have the same dimensions
d) The tile container is completely filled with no empty tiles-slots

My Attempts:

So at first I put a mousemove event listener on the tile container, and for every move the mouse makes, I loop through all tiles, find all whose edges lie in the proximity of the mouse, and apply the light up effect to them appropriately. But this started taking too much time as the number of tiles increased.

Then I tried using applying some calculations with the mouse coordinates relative to the tile container, and extracting "nearby" tiles based on the coordinates, but that's causing too many issues for me, perhaps due to some incorrect maths.

TL;DR:

Trying to figure out a fast-ish method to emulate Window's tile light-up-effect-thingy, even a crude copy is fine as long as it looks cool enough. Any assistance would be much appreciated:)

Here's an admittedly hacky solution. Though, if your menu has static dimensions, it should do the trick:

 const container = document.getElementById('container'); const glow = document.getElementById('glow'); container.addEventListener('mousemove', e => { glow.style.left = e.pageX + 'px'; glow.style.top = e.pageY + 'px'; }); container.addEventListener('mouseenter', () => { glow.style.display = 'block'; }) container.addEventListener('mouseleave', () => { glow.style.display = 'none'; })
 body { margin: 0; background-color: #111111; } #container { display: grid; grid-template-columns: repeat(3, 150px); grid-template-rows: repeat(3, 100px); gap: 10px; position: relative; width: fit-content; overflow: hidden; } #container>* { background: rgb(77, 77, 77); } #glow { position: absolute; top: 0; left: 0; width: 200px; height: 200px; background: radial-gradient(#fff, transparent 70%); transform: translate(-50%, -50%); z-index: -1; } #container.vertical { position: absolute; height: 100%; width: 8px; top: 0; background: #111111; }.vertical-one { left: 151px; }.vertical-two { left: 311px; } #container.horizontal { position: absolute; width: 100%; height: 8px; left: 0; background: #111111; }.horizontal-one { top: 101px; }.horizontal-two { top: 211px; }
 <div id="container"> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div id="glow"></div> <div class="vertical vertical-one"></div> <div class="vertical vertical-two"></div> <div class="horizontal horizontal-one"></div> <div class="horizontal horizontal-two"></div> </div>

It's essentially a div with a white radial-gradient which follows the cursor position as it moves. The hacky part is using bars which are the same color as the background in between each item. Not what I'd call a scalable solution, but, it works.

This border/background effect is called "reveal" and is part of the Fluent Design System. Luckily, you aren't the first wanting to implement this. You can take a look at this project: https://github.com/d2phap/fluent-reveal-effect . It's an implementation of the reveal effect and works pretty well.

If you want to solve it yourself, I think the mousemove event is the right one to choose. When the mouse moves, get the distance from the mouse position to every element with reveal effect and determine if they are near enough to have the reveal effect. If an element is near enough, just render a circular gradient on the position of the mouse and set that at the background/border for every element in the "reveal radius". This should work well enough for a few dozens items. In addition to mousemove, you will also need to handle mouseleave and mouseenter to not have the reveal be stuck at the border and you also need to listen to scrolling events as otherwise your reveal also will be stuck at the previous position.

An even more performant way of doing this (though I am not entirely sure how to implement it) would be to have an image where you draw the reveal effect as if it is one huge button. Then you just "cut out" the surface and set the background for buttons to cutouts of the image based on their position. The issue is creating the surface and applying it to your controls correctly so I would opt for the first option if there are not too many items.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM