Finished targetPreview

Reverted back to react logic instead of scss logic and improved performance by partially showing the preview icon instead of letting it follow the mouse directly.
This commit is contained in:
aronmal 2022-08-14 02:37:58 +02:00
parent 070597e6fb
commit 54df49341c
Signed by: aronmal
GPG key ID: 816B7707426FC612
4 changed files with 149 additions and 105 deletions

View file

@ -1,12 +1,23 @@
import { faBurst, faCrosshairs, faXmark } from '@fortawesome/free-solid-svg-icons'; import { faBurst, faCrosshairs, faXmark } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { CSSProperties, useState } from 'react'; import { CSSProperties, useEffect, useState } from 'react';
import { TargetPreviewType } from './interfaces';
import './styles/App.scss'; import './styles/App.scss';
function App() { function App() {
const [target, setTarget] = useState({ const [target, setTarget] = useState({
event: false, show: false,
x: 0,
y: 0
})
const [targetPreview, setTargetPreview] = useState<TargetPreviewType>({
newX: 0,
newY: 0,
shouldShow: false,
appearOK: false,
eventReady: true,
show: false,
x: 0, x: 0,
y: 0 y: 0
}) })
@ -43,38 +54,101 @@ function App() {
<div key={i} className='hit-svg' style={{'--x': obj.x, '--y': obj.y} as CSSProperties}> <div key={i} className='hit-svg' style={{'--x': obj.x, '--y': obj.y} as CSSProperties}>
<FontAwesomeIcon icon={faBurst} /> <FontAwesomeIcon icon={faBurst} />
</div>); </div>);
const corner = (x: number, y: number, count: number) => {switch (true) {
case x === 0 && y === 0:
return 'left-top-corner';
case x === count+1 && y === 0:
return 'right-top-corner';
case x === 0 && y === count+1:
return 'left-bottom-corner';
case x === count+1 && y === count+1:
return 'right-bottom-corner';
default:
return '';
}};
const border = (x: number, y: number, count: number) => {switch (true) {
case x === 0:
return 'left';
case y === 0:
return 'top';
case x === count+1:
return 'right';
case y === count+1:
return 'bottom';
default:
return '';
}};
for (let y = 0; y < count+2; y++) { for (let y = 0; y < count+2; y++) {
for (let x = 0; x < count+2; x++) { for (let x = 0; x < count+2; x++) {
const corner = [ const cornerReslt = corner(x, y, count);
x === 0 && y === 0 ? 'left-top-corner' : '', const borderType = cornerReslt ? cornerReslt : border(x, y, count);
x === count+1 && y === 0 ? 'right-top-corner' : '', const isGameTile = x > 0 && x < count+1 && y > 0 && y < count+1;
x === 0 && y === count+1 ? 'left-bottom-corner' : '', const classNames = ['border-tile'];
x === count+1 && y === count+1 ? 'right-bottom-corner' : '' if (borderType)
].filter(s => s); classNames.push('edge', borderType);
const border = [ if (isGameTile)
x === 0 ? 'left' : '', classNames.push('game-tile');
y === 0 ? 'top' : '',
x === count+1 ? 'right' : '',
y === count+1 ? 'bottom' : ''
].filter(s => s);
const borderType = corner.length ? corner[0] : border[0];
const action = x > 0 && x < count+1 && y > 0 && y < count+1;
const classNames = [
'border-tile',
borderType ? `edge ${borderType}` : '',
action ? 'action' : ''
].join(' ')
borderTiles.push( borderTiles.push(
<div <div
key={y*(count+2)+x} key={y*(count+2)+x}
className={classNames} className={classNames.join(' ')}
style={{'--x': (x + 1), '--y': (y + 1)} as CSSProperties} style={{'--x': (x + 1), '--y': (y + 1)} as CSSProperties}
onClick={action ? () => setTarget({ event: true, x, y }) : () => {}} onClick={() => {
if (isGameTile)
setTarget({ show: true, x, y });
}}
onMouseEnter={() => setTargetPreview(e => ({...e, newX: x, newY: y, shouldShow: isGameTile}))}
></div> ></div>
) )
} }
} }
// handle visibility and position change of targetPreview
useEffect(() => {
const {newX, newY, shouldShow, appearOK, eventReady, show, x, y} = targetPreview;
const positionChange = !(x === newX && y === newY);
// if not ready or no new position
if (!eventReady || !positionChange)
return;
if (show) {
// hide preview to change position when hidden
setTargetPreview(e => ({...e, appearOK: false, eventReady: false, show: false}));
} else if (shouldShow && appearOK) {
// BUT only appear again if it's supposed to (in case the mouse left over the edge) and ()
setTargetPreview(e => ({...e, appearOK: false, eventReady: false, show: true, x: newX, y: newY}));
}
}, [targetPreview])
// enable targetPreview event again after 200 mil. sec.
useEffect(() => {
if (targetPreview.eventReady)
return;
const autoTimeout = setTimeout(() => {
setTargetPreview(e => ({...e, eventReady: true}));
}, 200);
// or abort if state has changed early
return () => {
clearTimeout(autoTimeout);
}
}, [targetPreview.eventReady]);
// approve targetPreview new position after 200 mil. sec.
useEffect(() => {
// early return to start cooldown only when about to show up
if (!targetPreview.shouldShow)
return;
const autoTimeout = setTimeout(() => {
setTargetPreview(e => ({...e, appearOK: true}));
}, 350);
// or abort if movement is repeated early
return () => {
clearTimeout(autoTimeout);
}
}, [targetPreview.shouldShow, targetPreview.newX, targetPreview.newY]);
for (let i = 1; i <= 6; i++) { for (let i = 1; i <= 6; i++) {
shipElems.push( shipElems.push(
@ -103,10 +177,10 @@ function App() {
{/* <img className='fog-left' src={`/fog/fog2.png`} alt={`fog1.png`} /> {/* <img className='fog-left' src={`/fog/fog2.png`} alt={`fog1.png`} />
<img className='fog-right' src={`/fog/fog2.png`} alt={`fog1.png`} /> <img className='fog-right' src={`/fog/fog2.png`} alt={`fog1.png`} />
<img className='fog-middle' src={`/fog/fog4.png`} alt={`fog4.png`} /> */} <img className='fog-middle' src={`/fog/fog4.png`} alt={`fog4.png`} /> */}
<div className={`hit-svg target ${target.event ? 'show' : ''}`} style={{'--x': target.x+1, '--y': target.y+1} as CSSProperties}> <div className={`hit-svg target ${target.show ? 'show' : ''}`} style={{'--x': target.x+1, '--y': target.y+1} as CSSProperties}>
<FontAwesomeIcon icon={faCrosshairs} /> <FontAwesomeIcon icon={faCrosshairs} />
</div> </div>
<div className={`hit-svg target-preview`}> <div className={`hit-svg target-preview ${targetPreview.show && (target.x !== targetPreview.x || target.y !== targetPreview.y) ? 'show' : ''}`} style={{'--x': targetPreview.x+1, '--y': targetPreview.y+1} as CSSProperties}>
<FontAwesomeIcon icon={faCrosshairs} /> <FontAwesomeIcon icon={faCrosshairs} />
</div> </div>
</div> </div>

View file

@ -0,0 +1,10 @@
export interface TargetPreviewType {
newX: number,
newY: number,
shouldShow: boolean,
appearOK: boolean,
eventReady: boolean,
show: boolean,
x: number,
y: number
}

View file

@ -36,7 +36,7 @@
#game-frame { #game-frame {
// border: 1px solid orange; // border: 1px solid orange;
position: relative; // position: relative;
$height: 945px; $height: 945px;
$width: $height; $width: $height;
height: $height; height: $height;
@ -63,73 +63,7 @@
&.edge { &.edge {
border: 1px solid gray; border: 1px solid gray;
} }
@include gradient-edge;
&.left-top-corner {
-webkit-mask-image: -webkit-gradient(linear, right bottom,
left top, color-stop(0, rgba(0,0,0,1)), color-stop(0.5, rgba(0,0,0,0)));
}
&.right-top-corner {
-webkit-mask-image: -webkit-gradient(linear, left bottom,
right top, color-stop(0, rgba(0,0,0,1)), color-stop(0.5, rgba(0,0,0,0)));
}
&.left-bottom-corner {
-webkit-mask-image: -webkit-gradient(linear, right top,
left bottom, color-stop(0, rgba(0,0,0,1)), color-stop(0.5, rgba(0,0,0,0)));
}
&.right-bottom-corner {
-webkit-mask-image: -webkit-gradient(linear, left top,
right bottom, color-stop(0, rgba(0,0,0,1)), color-stop(0.5, rgba(0,0,0,0)));
}
&.left {
-webkit-mask-image: -webkit-gradient(linear, right top,
left top, from(rgba(0,0,0,1)), to(rgba(0,0,0,0)));
}
&.right {
-webkit-mask-image: -webkit-gradient(linear, left top,
right top, from(rgba(0,0,0,1)), to(rgba(0,0,0,0)));
}
&.top {
-webkit-mask-image: -webkit-gradient(linear, left bottom,
left top, from(rgba(0,0,0,1)), to(rgba(0,0,0,0)));
}
&.bottom {
-webkit-mask-image: -webkit-gradient(linear, left top,
left bottom, from(rgba(0,0,0,1)), to(rgba(0,0,0,0)));
}
$point-size: 1;
$count: 12;
$grid-elem-width: $count + 2;
$warst: $count + 1.5;
$one: calc($height / $warst);
$elements-in-grid: $grid-elem-width * $grid-elem-width;
// Inspired by https://stackoverflow.com/questions/35990505/get-position-of-mouse-pointer-in-css-without-javascript
@for $i from 1 through $elements-in-grid {
&:nth-child(#{$i}) {
&:hover {
&:not(.edge) ~ .target-preview {
opacity: 1;
}
& ~ .target-preview {
$row: ceil(calc($i / $grid-elem-width));
top: (
($row - 2)
* $one
+ ($one * 0.75)
);
left: (
($i
- (($row - 1) * $grid-elem-width)
- 2)
* $one
+ ($one * 0.75)
);
}
}
}
}
} }
> :not(.border) { > :not(.border) {
box-sizing: border-box; box-sizing: border-box;
@ -212,24 +146,15 @@
&.target { &.target {
color: red; color: red;
opacity: 0; opacity: 0;
&.show {
opacity: 1;
}
} }
&.target-preview { &.target-preview {
color: orange; color: orange;
opacity: 0; opacity: 0;
position: absolute;
@include transition(.2s); @include transition(.2s);
$count: 12; }
height: calc($height / ($count + 1.5)); &.show {
width: calc($height / ($count + 1.5)); opacity: 1;
&.show {
opacity: 1;
}
} }
} }
.r2 { .r2 {

View file

@ -16,3 +16,38 @@
opacity: 1; opacity: 1;
} }
} }
@mixin gradient-edge {
&.left-top-corner {
-webkit-mask-image: -webkit-gradient(linear, right bottom,
left top, color-stop(0, rgba(0,0,0,1)), color-stop(0.5, rgba(0,0,0,0)));
}
&.right-top-corner {
-webkit-mask-image: -webkit-gradient(linear, left bottom,
right top, color-stop(0, rgba(0,0,0,1)), color-stop(0.5, rgba(0,0,0,0)));
}
&.left-bottom-corner {
-webkit-mask-image: -webkit-gradient(linear, right top,
left bottom, color-stop(0, rgba(0,0,0,1)), color-stop(0.5, rgba(0,0,0,0)));
}
&.right-bottom-corner {
-webkit-mask-image: -webkit-gradient(linear, left top,
right bottom, color-stop(0, rgba(0,0,0,1)), color-stop(0.5, rgba(0,0,0,0)));
}
&.left {
-webkit-mask-image: -webkit-gradient(linear, right top,
left top, from(rgba(0,0,0,1)), to(rgba(0,0,0,0)));
}
&.right {
-webkit-mask-image: -webkit-gradient(linear, left top,
right top, from(rgba(0,0,0,1)), to(rgba(0,0,0,0)));
}
&.top {
-webkit-mask-image: -webkit-gradient(linear, left bottom,
left top, from(rgba(0,0,0,1)), to(rgba(0,0,0,0)));
}
&.bottom {
-webkit-mask-image: -webkit-gradient(linear, left top,
left bottom, from(rgba(0,0,0,1)), to(rgba(0,0,0,0)));
}
}