실시간 공동 작업 드로잉(3부): 웹킷 포스 시뮬레이션 및 캔버스와 자바스크립트로 색상 견본 사용

나는 우리에게 업그레이드를 제공하겠다고 약속했으므로 지금 그것을 살펴보자. 현재 다음과 같이 허용하지 않는 몇 가지 사항이 있습니다.
  • 색상 전환
  • 웹킷에서만 사용할 수 있는 압력 값
  • 단일 선 기반 브러시 도구

  • 색상 견본



    우리의 색상 생성은 단순히 로드 시 임의 함수였습니다. 여러 색상 세대와 라이브러리를 살펴보았는데 모두 너무 많았습니다. 특히 이러한 색상 선택기 라이브러리 중 다수는 CMYK, RGB, HSL, HSV와 같은 서로 다른 색 구성표 간 변환을 위한 코드를 추가합니다. 대신 더 쉬운(하나의 기능) 접근 방식은 단순히 견본을 만드는 것입니다(Google Docs와 유사).

    이를 달성하기 위해 실제로 필요한 것은 정의된 색상 배열을 갖는 것입니다.

    const swatch = [
        ["#000000", "#434343", "#666666", "#999999", "#b7b7b7", "#cccccc", "#d9d9d9", "#efefef", "#f3f3f3", "#ffffff"],
        ["#980000", "#ff0000", "#ff9900", "#ffff00", "#00ff00", "#00ffff", "#4a86e8", "#0000ff", "#9900ff", "#ff00ff"],
        ["#e6b8af", "#f4cccc", "#fce5cd", "#fff2cc", "#d9ead3", "#d0e0e3", "#c9daf8", "#cfe2f3", "#d9d2e9", "#ead1dc"],
        ["#dd7e6b", "#ea9999", "#f9cb9c", "#ffe599", "#b6d7a8", "#a2c4c9", "#a4c2f4", "#9fc5e8", "#b4a7d6", "#d5a6bd"],
        ["#cc4125", "#e06666", "#f6b26b", "#ffd966", "#93c47d", "#76a5af", "#6d9eeb", "#6fa8dc", "#8e7cc3", "#c27ba0"],
        ["#a61c00", "#cc0000", "#e69138", "#f1c232", "#6aa84f", "#45818e", "#3c78d8", "#3d85c6", "#674ea7", "#a64d79"],
        ["#85200c", "#990000", "#b45f06", "#bf9000", "#38761d", "#134f5c", "#1155cc", "#0b5394", "#351c75", "#741b47"],
        ["#5b0f00", "#660000", "#783f04", "#7f6000", "#274e13", "#0c343d", "#1c4587", "#073763", "#20124d", "#4c1130"]
    ];
    

    위의 색상 견본은 상대적으로 Google 문서에서 볼 수 있는 동일한 색상과 일치해야 합니다. 다음으로 이 견본에서 지정된 색상에 해당하는 div 목록을 생성해야 합니다.

    const colorMap = swatch.flat();
    
    let swatchContainer = document.querySelector('#color-picker');
    let colorElements = {};
    swatch.forEach(row => {
        let rowElem = document.createElement('div');
        rowElem.classList.add('hstack');
        row.forEach(c => {
            let elem = document.createElement('div');
            elem.classList.add('box');
            elem.style.backgroundColor = c;
            colorElements[c] = elem;
            rowElem.appendChild(elem);
        });
    
        swatchContainer.appendChild(rowElem);
    });
    

    단순한! 이제 현재 색상에 따라 상자가 활성화/비활성화되도록 이 코드에 클릭 동작을 추가해 보겠습니다.

    elem.onclick = function (e) {
        colorPicker.dataset.color = c;
        colorPicker.style.color = c;
        if (colorElements[color]) {
            colorElements[color].classList.remove('active');
        }
        color = c;
        elem.classList.toggle('active');
        e.preventDefault();
    };
    

    임의 색상이 rgb를 기반으로 색상을 생성하고 이를 위해 Math.random()를 사용했음을 상기하십시오. 기존 견본 내에서 임의의 색상을 생성하기 위해 해당 코드를 다음으로 대체합니다.

    function randomColor() {
        return parseInt(Math.random() * colorMap.length);
    }
    
    var colorIndex = randomColor();
    var color = colorMap[colorIndex];
    var colorPicker = document.querySelector('[data-color]');
    colorPicker.dataset.color = color;
    colorPicker.style.color = color;
    colorElements[color].classList.add('active');
    

    엄청난! 이 설정에 해당하는 html을 추가해 보겠습니다. 이상적으로 색상 선택기는 드롭다운으로 작동해야 합니다. 거기에 초기 html을 추가합시다.

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>Let's Draw Together</title>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/fonts/remixicon.css">
        <link rel="stylesheet" href="/static/index.css">
        <link rel="alternate icon" type="image/png" href="/static/logo.png">
        <link rel="icon" type="image/svg+xml" href="/static/logo.png">
    </head>
    <body>
        <div class="flush vstack">
            <div class="menubar hstack">
                <a class="icon-link center">
                    <i class="ri-lg ri-landscape-line"></i>
                </a>
                <div class="spacer"></div>
                <a class="icon-link active center" data-tool="pencil">
                    <i class="ri-lg ri-pencil-fill"></i>
                </a>
                <a class="icon-link center" data-tool="rect">
                    <i class="ri-lg ri-shape-line"></i>
                </a>
                <a class="icon-link center" data-tool="circle">
                    <i class="ri-lg ri-checkbox-blank-circle-line"></i>
                </a>
                <a class="icon-link center" data-tool="text">
                    <i class="ri-lg ri-font-size-2"></i>
                </a>
                <div class="spacer"></div>
                <div class="relative">
                    <a class="icon-link center" data-color="#33ffff">
                        <i class="ri-lg ri-palette-line"></i>
                        <i class="ri-lg ri-checkbox-blank-fill center"></i>
                    </a>
                    <div id="color-picker" class="dropdown vstack">
                    </div>
                </div>
                <div class="spacer"></div>
            </div>
            <div class="spacer app">
                <canvas></canvas>
            </div>
        </div>
    
        <script type="text/javascript" src="/static/load.js"></script>
        <script type="text/javascript" src="/static/draw.js"></script>
    </body>
    </html>
    

    데이터 색상 링크를 relative 클래스로 래핑하고 있습니다. 이를 처리할 해당 클래스가 있는지 확인하겠습니다.

    .relative {
        position: relative;
    }
    

    복제하고 싶은 동작은 마우스 오버 시 드롭다운이 표시된다는 것입니다(견본에 마우스를 올리거나 이 데이터 색상 링크에 마우스를 올려 놓으면).

    :root {
        /** .... */
        --dropdown-background: #fff;
        --dropdown-shadow:  0px 0px 1px 0px rgba(0,0,0,0.5), 0px 2px 6px -5px rgba(0,0,0,0.75);
    }
    
    .dropdown {
        position: absolute;
        background-color: var(--dropdown-background);
        padding: 4px;
        box-shadow: var(--dropdown-shadow);
        border-radius: 4px;
        z-index: 1;
        margin-left: -80px;
        transition: all 0.25s ease-in-out;
    }
    .icon-link + .dropdown {
        opacity: 0;
        top: 8px;
        visibility: hidden;
        transition: all 0.25s ease-in-out;
    }
    .icon-link:hover + .dropdown, .dropdown:hover {
        opacity: 1;
        top: 16px;
        visibility: visible;
    }
    .icon-link:hover + .dropdown *, .dropdown:hover * {
        opacity: 1;
    }
    

    마지막으로 호버/레이아웃을 처리하려면 상자 클래스가 필요합니다.

    /** Color Picker */
    .box {
        width: 24px;
        height: 24px;
        cursor: pointer;
    }
    .box:hover, .box.active {
        box-shadow: inset 0px 0px 2px 2px #fff;
        opacity: 0.75;
    }
    

    엄청난! 이제 색상 견본 도구가 생겼습니다!



    Webkit Force 시뮬레이션



    Safari에 있는 경우 onwebkitmouseforcechanged 이벤트의 webkitForce 속성을 사용하여 현재 트랙패드 압력 값을 얻을 수 있습니다. 이것은 독점적이기 때문에 Safari(또는 데스크탑 앱용 Swift)에서 작업하지 않고는 이 값에 액세스할 수 있는 실제 방법이 없습니다. 그러나 키 누르기 값을 사용하여 힘 값을 높이거나 낮추면 이러한 종류의 값을 시뮬레이션할 수 있습니다. 이는 커서 주위를 이동할 때 변경 사항을 다소 모방합니다.

    var force = 1;
    var mouseDown = false;
    
    function move(e) {
        mouseDown = e.buttons;
        /** ... */
    }
    
    function key(e) {
        if (e.key === 'Backspace') {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
        }
        if (mouseDown && e.key === 'ArrowUp') {
            force += 0.025;
        }
        if (mouseDown && e.key === 'ArrowDown') {
            force -= 0.025;
        }
    }
    
    window.onkeydown = key;
    

    이제 위 또는 아래 키를 누를 때마다 마우스를 누르고 있는 동안 값을 변경할 수 있습니다!



    왼쪽/오른쪽 화살표 키로 색상 변경



    한 가지 더 추가하고 싶은 것은 키를 누를 때 색상을 변경할 수 있는 기능입니다. 이를 위해 현재 색상 인덱스를 간단히 업데이트할 수 있습니다.

    function key(e) {
        if (e.key === 'Backspace') {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
        }
        if (e.key === 'ArrowRight') {
            colorIndex++;
        }
        if (e.key === 'ArrowLeft') {
            colorIndex--;
        }
        if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
            if (colorIndex >= colorMap.length) {
                colorIndex = 0;
            }
            if (colorIndex < 0) {
                colorIndex = colorMap.length - 1;
            }
            if (colorElements[color]) {
                colorElements[color].classList.remove('active');
            }
            color = colorMap[colorIndex];
            colorPicker.dataset.color = color;
            colorPicker.style.color = color;
            colorElements[color].classList.toggle('active');
        }
        if (mouseDown && e.key === 'ArrowUp') {
            force += 0.025;
        }
        if (mouseDown && e.key === 'ArrowDown') {
            force -= 0.025;
        }
    }
    



    동시에 결합하는 것은 어떻습니까? 이렇게 하려면 강제 변경 키를 SHIFT 및 ALT와 같은 키로 변경해야 합니다. 우리는 여전히 위쪽/아래쪽 화살표로 제어할 수 있기를 원하고 왼쪽 또는 오른쪽을 누를 때만 shift/alt를 제한하려고 합니다.

        if (mouseDown && (e.key === 'ArrowUp' || (e.shiftKey && ['ArrowLeft', 'ArrowRight'].includes(e.key)))) {
            force += 0.025;
        }
        if (mouseDown && (e.key === 'ArrowDown' || (e.altKey && ['ArrowLeft', 'ArrowRight'].includes(e.key)))) {
            force -= 0.025;
        }
    



    대박! 이 기사가 마음에 드셨다면 팔로우와 좋아요를 눌러주세요. 또한 물리학, 코드, cs 이론 등에 관한 유사하고 흥미로운 내용을 게시한 내 위치를 자유롭게 확인하십시오!

    건배! 🍻

    암호



    이 시리즈의 코드에 관심이 있다면 아래 GitHub에서 제 저장소를 확인하세요.

    https://github.com/nyxtom/drawing-webrtc

    좋은 웹페이지 즐겨찾기