惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
About on SuperTechFans
量子位
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 聂微东
V
Visual Studio Blog
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 司徒正美
V
V2EX
The GitHub Blog
The GitHub Blog
博客园_首页
月光博客
月光博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
MyScale Blog
MyScale Blog
博客园 - 叶小钗
F
Fortinet All Blogs
T
Tailwind CSS Blog
GbyAI
GbyAI
酷 壳 – CoolShell
酷 壳 – CoolShell
IT之家
IT之家
WordPress大学
WordPress大学
B
Blog
H
Help Net Security

博客园 - Zion0707

Codex接入DeepSeek token实现对话与操作,so easy! Openclaw只能聊天不能操作解决方案 Ubuntu从零搭建Openclaw Openclaw安装问题记录 React数字滚动,增加或减少效果 react hooks实现对元素拖拽及鼠标滚轮缩放 前端实现图片文字识别并提取 ECharts实现两条曲线数据比较,数据高出区域高亮显示 canvas实现视频播放并支持自动播放 fabricjs实现虚线流动动画效果 React国际化方案及示例 fabricjs如何导入echarts fabricjs元素对齐方式实现 萤石云视频监控和回放方案 react-router-dom v6 使用 “DatePicker”不能用作 JSX 组件。 Element plus的tree组件实现单选和搜索功能 16进制相关操作方法 js 版 AES-128 算法加解密 js 版 React 下拉多选,全选/全不选功能组件
React仿photoshop参考线功能
Zion0707 · 2022-11-10 · via 博客园 - Zion0707

功能实现:React仿photoshop参考线功能,标尺处拉出参考线,双击参考线或上拉至标尺中消失。

功能截图:

 index.jsx:

import React, { useRef, useEffect, useState } from 'react';
import { v4 } from 'uuid';
import './index.less';

const createUUID = () => {
    return v4();
};

// 单条参考线组件
const GuidesLine = (props) => {
    const { id, top, removeCb, updateCb } = props;
    const dragRef = useRef(null);

    useEffect(() => {
        const drag = dragRef.current;
        drag.onmousedown = (ev) => {
            ev.preventDefault();
            const diffY = ev.clientY - drag.offsetTop;
            document.onmousemove = (ev) => {
                ev.preventDefault();
                ev.stopPropagation();
                let moveY = ev.clientY - diffY;
                drag.style.top = moveY + 'px';
            };
            document.onmouseup = (ev) => {
                document.onmousemove = null;
                document.onmouseup = null;
                // 如果小于等于20则去除参考线
                if (ev.clientY <= 20) {
                    removeCb(id);
                } else {
                    updateCb(id, ev.clientY);
                }
            };
        };

        return () => {
            drag.onmousedown = null;
        };
    }, [props]);

    return (
        <div
            ref={dragRef}
            className="guides-line guides-line__h"
            data-id={id}
            style={{ top: `${top}px` }}
            onDoubleClick={() => {
                // 双击删除
                removeCb(id);
            }}
        ></div>
    );
};

// 坐标尺
const Ruler = () => {
    const rulerRef = useRef(null);
    const [guidesLineList, setGuidesLineList] = useState([]); //参考线列表
    const [virtualGuidesTop, setVirtualGuidesTop] = useState(0); //虚拟参考线距离顶部高度

    useEffect(() => {
        const el = rulerRef.current;
        el.onmousedown = (ev) => {
            setVirtualGuidesTop(ev.clientY);
            document.onmousemove = (ev) => {
                ev.preventDefault();
                setVirtualGuidesTop(ev.clientY);
            };
            document.onmouseup = (ev) => {
                document.onmousemove = null;
                document.onmouseup = null;
                setVirtualGuidesTop(0);
                if (ev.clientY > 20) {
                    // 添加新的参考线
                    const list = [
                        ...guidesLineList,
                        {
                            id: createUUID(),
                            top: ev.clientY,
                        },
                    ];
                    console.log(list);
                    setGuidesLineList(list);
                }
            };
        };

        return () => {
            el.onmousedown = null;
        };
    }, [guidesLineList]);

    return (
        <>
            <div className="ruler" ref={rulerRef}>
                {virtualGuidesTop !== 0 && virtualGuidesTop > 20 && <div className="virtual-guides-line guides-line-h" style={{ top: `${virtualGuidesTop}px` }}></div>}
            </div>

            {/* 渲染参考线列表 */}
            {guidesLineList.map((item) => {
                return (
                    <GuidesLine
                        key={item.id}
                        id={item.id}
                        top={item.top}
                        removeCb={(id) => {
                            setGuidesLineList(guidesLineList.filter((item) => item.id !== id));
                        }}
                        updateCb={(id, top) => {
                            console.log(id);
                            setGuidesLineList(
                                guidesLineList.map((item) => {
                                    if (item.id === id) {
                                        item.top = top;
                                    }
                                    return item;
                                })
                            );
                        }}
                    />
                );
            })}
        </>
    );
};

export default Ruler;

index.less

* {
    padding: 0;
    margin: 0;
}
// 标尺样式
.ruler {
    width: 100%;
    height: 20px;
    background-color: #ccc;
    border-bottom: 1px solid #000;
    position: absolute;
    z-index: 1;
    .virtual-guides-line {
        height: 1px;
        width: 100%;
        background-color: #aaa;
        position: absolute;
        left: 0;
        top: 0;
    }
}

// 参考线样式
.guides-line {
    position: absolute;
    &.guides-line__h {
        left: 0;
        top: 0;
        height: 5px;
        width: 100%;
        &:hover {
            cursor: n-resize;
        }
        &::before {
            content: ' ';
            display: block;
            background-color: aqua;
            height: 1px;
            width: 100%;
        }
    }
}