使用视图宽度和高度定位的问题

问题描述 投票:0回答:1

我正在尝试解决 React 挑战。挑战是当你点击屏幕上的某个地方时,你点击的地方会出现一个圆圈。您还有一个重做和撤消按钮。问题发生在以下限制条件下:

  1. 圆圈必须是响应式的,所以当屏幕宽度改变时它们会改变它们的大小(宽度越小,圆圈越小)。
  2. 必须在光标所在的位置创建一个圆,因此光标位于圆的中心,具有任意屏幕宽度和高度。
  3. 当您单击靠近撤消/重做按钮或边框时,将跳过第二条规则,因为您需要圆圈完全位于屏幕内。

它应该是这样的:

我解决了第一个,宽度和高度等于 vw 中的相同值。 我还解决了第二个从位置减去圆宽度的一半。 但是对于最后一个,我在顶部和底部边界上遇到了问题。当屏幕宽度改变时,圆圈会移动。

顶部和底部的圆圈应该分别粘在按钮和底部边框上,但它们不是。

这是我的代码:

App.tsx

export interface IPoint {
    x: number
    y: number
    size: number
    color: string
}

const pointSize = 5

export const App: FC = () => {
    const [points, setPoints] = useState<IPoint[]>([])

    const controlsRef = useRef<HTMLDivElement>(null)

    const handlePlacePoint = (e: React.MouseEvent) => {
        const offsetX = pointSize / 2
        const offsetY = pointSize / 2
        let x = (e.clientX / window.innerWidth) * 100
        let y = (e.clientY / window.innerHeight) * 100

        const controlsHeight =
            (controlsRef.current?.offsetHeight! / window.innerHeight) * 100

        if (x <= offsetX) {
            x = offsetX
        } else if (x >= 100 - offsetX) {
            x = 100 - offsetX
        }

        if (y <= controlsHeight + offsetY) {
            y = controlsHeight + offsetY
        } else if (y >= 100 - offsetY) {
            y = 100 - offsetY
        }

        const newPoint: IPoint = {
            x,
            y,
            color: generateColor(),
            size: pointSize
        }

        setPoints([...points, newPoint])
    }

    return (
        <main>
            <div className='controls' ref={controlsRef}>
                <button>Undo ↺</button>
                <button>Redo ↻</button>
            </div>
            <div
                className='points-panel'
                onClick={handlePlacePoint}
                style={{ backgroundColor: '#8b3ae0' }}
            >
                {points.map(point => (
                    <Point key={Math.random()} {...point} />
                ))}
            </div>
        </main>
    )
}

Point.tsx

export const Point: FC<IPoint> = ({ x, y, size, color }) => {
    return (
        <div
            className='point'
            style={{
                backgroundColor: color,
                top: `calc(${y}vh - ${size / 2}vw)`,
                left: `calc(${x}vw - ${size / 2}vw)`,
                width: `${size}vw`,
                height: `${size}vw`
            }}
        />
    )
}

App.css

.controls {
    position: absolute;
    z-index: 1;
    width: 100vw;
    height: 5vh;
    display: flex;
}

.controls button {
    flex-grow: 1;
    font-size: 25px;
}

.pointsPanel {
    position: relative;
    height: 100vh;
    width: 100vw;
}

.point {
    position: absolute;
    border-radius: 100%;
    border: 2px solid #000;
}

index.css

html,
body, 
* {
    margin: 0;
    box-sizing: border-box;
}

body {
    overflow: hidden;
}

我尝试了很多不同的方法,但我想我错过了 vh 中的 Point top 值减去 vw 中的值。我不知道如何改变它而不是破坏已经有效的东西。你能告诉我如何修复它以便它满足所有 3 个要求吗?或者也许有更干净的方法?

css reactjs responsive-design position responsive
1个回答
0
投票

您忽略了 React 仍然只是 JavaScript,并且在具有完整 CSS 支持的浏览器中运行,从而使事情过于复杂。通过使用原生 JS 函数,并依靠 CSS 来放置东西,你需要的一堆东西可以更简单地完成:

import { useState, useRef } from "react";
import Circle from "./Circle.jsx";
import "./App.css";

export default function App() {
  const field = useRef();
  const [points, setPoints] = useState([]);

  const placeCircle = e => {
    const { offsetX, offsetY } = e.nativeEvent;
    const { width, height } = field.current.getBoundingClientRect();
    points.push({
      id: Date.now(),
      x: offsetX / width,
      y: offsetY / height,
    });
    setPoints(points);
  };

  const circles = points.map(p => <Circle key={p.id} {...p} />);

  return (
    <main>
      <div className='panel' onClick={placeCircle} ref={field}>
        {circles}
      </div>
    </main>
  );
}

原生点击事件有一个

offsetX
offsetY
,这正是您在此处获取 div inside 的点击位置所需要的。所以我们使用
event.nativeEvent
来获取它们,然后将它们从坐标转换为百分比,我们只需将它们除以父 div 的宽度和高度。使用每个 HTML 元素都有的
getBoundingClientRect()
函数,这在普通 JS 中又是微不足道的。

然后我们将我们的

Circle
定义为一个非常非常愚蠢的东西:

import { useState, useRef } from "react";
import "./Circle.css";

export default function Circle(props) {
  const sizing = {
    "--x": props.x,
    "--y": props.y,
    "--size": props.size ?? "1em",
  };
  return <div className="circle" style={sizing}/>;
}

所以是的,它什么都不做,它只是转发我们需要的值作为 CSS 变量,我们不会让 JS 在这里做anything。相反,我们让浏览器为我们做这件事:

.circle {
  position: absolute;
  top: calc(100% * var(--y));
  left: calc(100% * var(--x));
  width: var(--size);
  height: var(--size);
  margin-top: calc(var(--size) / -2);
  margin-left: calc(var(--size) / -2);
  background: red;
  border: 2px solid #000;
  border-radius: 100%;
}

我们完成了。

© www.soinside.com 2019 - 2024. All rights reserved.