JavaScript, HTML 및 CSS로 스케치 플러그인을 빌드하는 방법(2부)

게시 됨: 2022-03-10
빠른 요약 ↬ Sketch 플러그인 구축에 대한 튜토리얼의 두 번째 부분에서는 사용자 인터페이스 구축을 중단한 부분을 선택한 다음 실제로 레이어 모자이크를 생성하는 핵심 기능으로 넘어갈 것입니다. 최종 플러그인 코드 최적화.

1부에서 언급했듯이 이 튜토리얼은 Sketch 앱을 알고 사용하며 코드를 사용하는 것을 두려워하지 않는 사람들을 대상으로 합니다. 이를 최대한 활용하려면 JavaScript(및 선택적으로 HTML/CSS) 작성에 대한 최소한의 기본 경험이 필요합니다.

이 튜토리얼의 이전 부분에서는 플러그인을 구성하는 기본 파일과 플러그인의 사용자 인터페이스를 만드는 방법에 대해 배웠습니다. 이 두 번째이자 마지막 부분에서는 사용자 인터페이스를 핵심 플러그인 코드에 연결하는 방법과 플러그인의 주요 기능을 구현하는 방법을 배웁니다. 마지막으로 코드를 최적화하는 방법과 플러그인이 작동하는 방식도 배우게 됩니다.

플러그인의 사용자 인터페이스 구축: 웹 인터페이스와 스케치 플러그인 코드가 서로 "대화"하도록 만들기

다음으로 해야 할 일은 웹 인터페이스와 Sketch 플러그인 간의 통신을 설정하는 것입니다.

웹 인터페이스의 "적용" 버튼을 클릭하면 웹 인터페이스에서 Sketch 플러그인으로 메시지를 보낼 수 있어야 합니다. 이 메시지는 단계 수, 회전 양, 생성할 복제 수 등과 같이 사용자가 입력한 설정에 대해 알려야 합니다.

WKWebView 를 사용하면 이 작업이 조금 더 쉬워집니다. window.webkit.messageHandlers API를 사용하여 웹 인터페이스의 JavaScript 코드에서 Sketch 플러그인으로 메시지를 보낼 수 있습니다.

Sketch 코드 쪽에서 다른 메서드인 addScriptMessageHandler:name: (또는 addScriptMessageHandler_name )을 사용하여 플러그인 웹 인터페이스에서 보낸 메시지를 수신할 때마다 호출될 메시지 핸들러를 등록할 수 있습니다.

먼저 웹 UI에서 메시지를 수신할 수 있는지 확인합니다. ui.js 파일의 createWebView 함수로 이동하여 다음을 추가합니다.

 function createWebView(pageURL){ const webView = WKWebView.alloc().init(); // Set handler for messages from script const userContentController = webView.configuration().userContentController(); const ourMessageHandler = ... userContentController.addScriptMessageHandler_name( ourMessageHandler, "sketchPlugin" ); // Load page into web view webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent()); return webView; };

여기에서 웹 보기의 userContentController 속성을 사용하여 "sketchPlugin"이라는 메시지 처리기를 추가합니다. 이 "사용자 콘텐츠 컨트롤러"는 메시지가 웹 보기에서 전달되도록 하는 다리입니다.

점프 후 더! 아래에서 계속 읽기 ↓

위의 코드에서 이상한 점을 눈치채셨을 수도 있습니다. 메시지 핸들러로 추가하려는 객체 ourMessageHandler 가 아직 존재하지 않습니다! 불행히도 이 메서드는 특정 종류의 기본 개체를 예상하기 때문에 일반 JavaScript 개체나 함수를 핸들러로 사용할 수 없습니다.

운 좋게도 우리는 MochaJSDelegate 를 사용하여 이 제한을 해결할 수 있습니다. Sketch/MochaJSDelegate.js 아래의 플러그인 번들에 수동으로 다운로드하여 저장해야 합니다.

그것을 사용하려면 먼저 ui.js 로 가져와야 합니다. 파일 맨 위에 다음을 추가합니다.

 const MochaJSDelegate = require("./MochaJSDelegate");

이제 MochaJSDelegate 를 사용하여 addScriptMessageHandler:name: 이 기대하는 메시지 핸들러 유형을 생성할 수 있습니다.

 function createWebView(pageURL){ const webView = WKWebView.alloc().init(); // Set handler for messages from script const userContentController = webView.configuration().userContentController(); const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { /* handle message here */ } }).getClassInstance(); userContentController.addScriptMessageHandler_name( scriptMessageHandler, "sketchPlugin" ); // Load page into web view webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent()); return webView; };

방금 추가한 코드는 필요한 기본 개체를 생성합니다. 또한 userContentController:didReceiveScriptMessage: 라는 개체에 대한 메서드를 정의합니다. — 이 메서드는 두 번째 인수로 원하는 메시지와 함께 호출됩니다. 아직 실제로 메시지를 보내지 않기 때문에 나중에 여기로 돌아와 실제로 받은 메시지를 구문 분석하고 처리하는 코드를 추가해야 합니다.

다음으로 이러한 메시지를 보내기 위해 웹 인터페이스에 일부 코드를 추가해야 합니다. /Resources/web-ui/script.js 로 이동합니다. 사용자가 옵션을 입력할 HTML <inputs /> 값 검색을 처리하는 대부분의 코드를 이미 작성했음을 알 수 있습니다.

아직 우리가 해야 할 일은 Sketch 코드에 값을 실제로 보내는 코드를 추가하는 것입니다.

apply 함수를 찾아 끝에 다음을 추가합니다.

 // Send user inputs to sketch plugin window.webkit.messageHandlers.sketchPlugin.postMessage(JSON.stringify({ stepCount, startingOptions, stepOptions }));

여기서 우리는 앞에서 언급한 window.webkit.messageHandlers API를 사용하여 위에서 sketchPlugin 으로 등록한 메시지 핸들러에 액세스합니다. 그런 다음 사용자의 입력이 포함된 JSON 문자열과 함께 메시지를 보냅니다.

모든 것이 올바르게 설정되었는지 확인합시다. /Sketch/ui.js 로 돌아갑니다. 예상대로 메시지를 받고 있는지 확인하기 위해 메시지를 받을 때 대화 상자를 표시하도록 이전에 정의한 메서드를 수정합니다.

 function createWebView(pageURL){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const UI = require("sketch/ui"); UI.alert("Hey, a message!", wkMessage.body()); } }).getClassInstance(); userContentController.addScriptMessageHandler_name( scriptMessageHandler, "sketchPlugin" ); // ... };

이제 플러그인을 실행하고(열린 기존 Mosaic 창을 먼저 닫아야 할 수도 있음) 일부 값을 입력한 다음 "적용"을 클릭합니다. 아래와 같은 경고가 표시되어야 합니다. 이는 모든 것이 올바르게 연결되었으며 메시지가 성공적으로 통과되었음을 의미합니다! 그렇지 않은 경우 이전 단계로 돌아가 설명된 대로 모든 작업이 완료되었는지 확인합니다.

플러그인 UI에서 '적용' 버튼을 클릭한 후 표시되는 대화 상자를 보여주는 이미지입니다.
적용을 클릭하면 표시되는 대화 상자가 나타납니다. (큰 미리보기)

이제 인터페이스에서 플러그인으로 메시지를 보낼 수 있으므로 해당 정보로 실제로 유용한 작업을 수행하는 코드 작성으로 넘어갈 수 있습니다. 바로 레이어 모자이크 생성입니다.

레이어 모자이크 생성

이를 위해 필요한 사항을 살펴보겠습니다. 조금 단순화하면 코드에서 수행해야 하는 작업은 다음과 같습니다.

  1. 현재 문서를 찾습니다.
  2. 현재 문서의 선택된 레이어를 찾습니다.
  3. 선택한 레이어( 템플릿 레이어라고 함)를 x 번 복제합니다.
  4. 각 복제본에 대해 사용자가 설정한 특정 값(양)으로 위치, 회전, 불투명도 등을 조정합니다.

이제 합리적인 계획이 수립되었으므로 계속해서 작성해 보겠습니다. 코드를 모듈화하는 패턴을 고수하면서 Sketch/ 폴더에 새로운 파일인 mosaic.js 를 만들고 여기에 다음 코드를 추가해 보겠습니다.

 function mosaic(options){ }; module.export = mosaic;

이 함수를 가져오면 API를 더 간단하게 사용할 수 있으므로 이 모듈의 유일한 내보내기로 이 함수를 사용할 것입니다. 웹 인터페이스에서 얻는 옵션이 무엇이든 mosaic() 를 호출하면 됩니다.

수행해야 할 처음 두 단계는 현재 문서를 가져온 다음 선택한 레이어를 가져오는 것입니다. Sketch API에는 Sketch sketch/dom 모듈을 가져와 액세스할 수 있는 문서 조작을 위한 내장 라이브러리가 있습니다. 지금은 Document 객체만 필요하므로 명시적으로 꺼내겠습니다. 파일 맨 위에 다음을 추가합니다.

 const { Document } = require("sketch/dom");

Document 객체에는 getSelectedDocument() 라는 우리가 사용할 수 있는 현재 문서에 액세스하기 위한 특정 메서드가 있습니다. 현재 문서 인스턴스가 있으면 문서의 selectedLayers 속성을 통해 사용자가 선택한 레이어에 액세스할 수 있습니다. 그러나 우리의 경우 단일 레이어 선택에만 관심이 있으므로 사용자가 선택한 첫 번째 레이어만 가져옵니다.

 function mosaic(options){ const document = Document.getSelectedDocument(); const selectedLayer = document.selectedLayers.layers[0]; }; module.export = mosaic;

참고: selectedLayers 자체가 배열일 것으로 예상했지만 그렇지 않습니다. 대신 Selection 클래스의 인스턴스입니다. 여기에는 이유가 있습니다. Selection 클래스에는 clear, map, reduce 및 forEach와 같이 선택 항목을 조작하기 위한 유용한 도우미 메서드가 많이 포함되어 있습니다. layer 속성을 통해 실제 레이어 배열을 노출합니다.

사용자가 문서를 여는 것을 잊거나 무언가를 선택하는 것을 잊어버린 경우를 대비하여 몇 가지 경고 피드백을 추가해 보겠습니다.

 const UI = require("sketch/ui"); function mosaic(options){ const document = Document.getSelectedDocument(); // Safety check: if(!document){ UI.alert("Mosaic", "️ Please select/focus a document."); return; } // Safety check: const selectedLayer = document.selectedLayers.layers[0]; if(!selectedLayer){ UI.alert("Mosaic", "️ Please select a layer to duplicate."); return; } }; module.export = mosaic;

이제 1단계와 2단계(현재 문서 및 선택한 레이어 찾기)에 대한 코드를 작성했으므로 3단계와 4단계를 처리해야 합니다.

  • 템플릿 레이어를 x 번 복제합니다.
  • 각 복제본에 대해 사용자가 설정한 특정 값으로 위치, 회전, 불투명도 등을 조정합니다.

복제 횟수, 시작 옵션 및 단계 옵션과 같은 options 에서 필요한 모든 관련 정보를 가져와 시작하겠습니다. 이전에 Document 에서 했던 것처럼 다시 한 번 구조 해제를 사용하여 options 에서 해당 속성을 가져올 수 있습니다.

 function mosaic(options) { // ... // Destructure options: var { stepCount, startingOptions, stepOptions } = options; }

다음으로 입력을 삭제하고 걸음 수가 항상 1 이상인지 확인합니다.

 function mosaic(options) { // ... // Destructure options: var { stepCount, startingOptions, stepOptions } = options; stepCount = Math.max(1, stepCount); }

이제 템플릿 레이어의 불투명도, 회전 등이 모두 사용자가 원하는 시작 값과 일치하는지 확인해야 합니다. 사용자의 옵션을 레이어에 적용하는 것은 우리가 많이 할 것이기 때문에 이 작업을 고유한 방법으로 옮깁니다.

 function configureLayer(layer, options, shouldAdjustSpacing){ const { opacity, rotation, direction, spacing } = options; layer.style.opacity = opacity / 100; layer.transform.rotation = rotation; if(shouldAdjustSpacing){ const directionAsRadians = direction * (Math.PI / 180); const vector = { x: Math.cos(directionAsRadians), y: Math.sin(directionAsRadians) }; layer.frame.x += vector.x * spacing; layer.frame.y += vector.y * spacing; } };

그리고 간격은 템플릿 레이어가 아닌 복제본 사이에만 적용하면 되므로 템플릿 레이어에 옵션을 적용하는지 아니면 옵션을 적용하는지에 따라 true 또는 false 로 설정할 수 있는 특정 플래그 shouldAdjustSpacing 을 추가했습니다. 아니다. 그렇게 하면 회전과 불투명도가 템플릿에 적용되지만 간격은 적용되지 않도록 할 수 있습니다.

mosaic 방법으로 돌아가서 이제 시작 옵션이 템플릿 레이어에 적용되었는지 확인합니다.

 function mosaic(options){ // ... // Configure template layer var layer = group.layers[0]; configureLayer(layer, startingOptions, false); }

다음으로 복제본을 생성해야 합니다. 먼저 현재 복제본에 대한 옵션이 무엇인지 추적하는 데 사용할 수 있는 변수를 생성해 보겠습니다.

 function mosaic(options){ // ... var currentOptions; // ... }

템플릿 레이어에 시작 옵션을 이미 적용했으므로 방금 적용한 옵션을 선택하고 다음 레이어에 적용할 옵션을 가져오기 위해 stepOptions 의 상대 값을 추가해야 합니다. 루프에서 이 작업을 몇 번 더 수행할 것이므로 이 작업을 특정 메서드인 stepOptionsBy 로 이동합니다.

 function stepOptionsBy(start, step){ const newOptions = {}; for(let key in start){ newOptions[key] = start[key] + step[key]; } return newOptions; };

그런 다음 이전 레이어를 복제하는 루프를 작성하고 여기에 현재 옵션을 적용한 다음 다음 복제에 대한 옵션을 얻기 위해 현재 옵션을 오프셋(또는 "단계")해야 합니다.

 function mosaic(options) { // ... var currentOptions = stepOptionsBy(startingOptions, stepOptions); for(let i = 0; i < (stepCount - 1); i++){ let duplicateLayer = layer.duplicate(); configureLayer(duplicateLayer, currentOptions, true); currentOptions = stepOptionsBy(currentOptions, stepOptions); layer = duplicateLayer; } }

모든 작업이 완료되었습니다. 플러그인이 수행해야 하는 작업의 핵심을 성공적으로 작성했습니다! 이제 사용자가 실제로 "적용" 버튼을 클릭할 때 모자이크 코드가 호출되도록 연결해야 합니다.

ui.js 로 돌아가서 메시지 처리 코드를 조정해 보겠습니다. 우리가 해야 할 일은 우리가 실제로 사용할 수 있는 객체로 바뀌도록 우리가 얻는 옵션의 JSON 문자열을 구문 분석하는 것입니다. 이러한 옵션이 있으면 해당 옵션으로 mosaic 기능을 호출할 수 있습니다.

먼저, 파싱. 받은 JSON 메시지를 구문 분석하려면 메시지 처리 기능을 업데이트해야 합니다.

 function createWebView(pageURL){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); } }); }

다음으로 이것을 mosaic 함수에 전달해야 합니다. 그러나 이것은 실제로 ui.js 의 코드가 수행해야 하는 작업이 아닙니다. 이것은 주로 모자이크 자체를 생성하는 것이 아니라 화면에 인터페이스 관련 항목을 표시하는 데 필요한 것과 관련되어야 합니다. 이러한 책임을 별도로 유지하기 위해 함수를 취하는 두 번째 인수를 createWebView 에 추가하고 웹 인터페이스에서 옵션을 수신할 때마다 해당 함수를 호출합니다.

이 인수의 이름을 onApplyMessage 지정하겠습니다.

 function createWebView(pageURL, onApplyMessage){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); onApplyMessage(message); } }); }

또한 내보낸 메서드 loadAndShow 를 수정하여 이 onApplyMessage 인수도 가져와서 createWebView 에 전달해야 합니다.

 function loadAndShow(baseURL, onApplyMessage){ // ... const webView = createWebView(pageURL, onApplyMessage); }

마지막으로 main.js 로 이동합니다. 이제 mosaic 기능을 가져와 플러그인의 사용자 인터페이스에서 받은 옵션으로 호출해야 합니다.

 const mosaic = require("./mosaic"); function onRun(context){ UI.loadAndShow(context.scriptURL, options => { mosaic(options); }); };

거의 완료되었습니다!

그러나 지금 코드를 실행하고 플러그인 인터페이스에서 "적용" 버튼을 클릭하면 아무 일도 일어나지 않습니다. 왜요? 그 이유는 Sketch 스크립트가 실행되는 방식 때문입니다. 기본적으로 스크립트의 맨 아래에 도달할 때까지만 스크립트가 "살아 있습니다". 그 이후에는 Sketch가 스크립트를 파괴하고 사용 중인 리소스를 해제합니다.

이것은 메시지 수신과 같이 비동기적으로 발생해야 하는 모든 작업(이 경우 코드의 맨 아래에 도달한 후)이 스크립트가 파괴되었기 때문에 수행할 수 없음을 의미하기 때문에 우리에게 문제입니다. 이것은 우리가 메시지를 받고 응답할 시간이 없기 때문에 웹 인터페이스에서 메시지를 받지 못한다는 것을 의미합니다!

Fibers 를 사용하여 이 시점 이후에도 스크립트가 유지되어야 한다는 신호를 Sketch에 보내는 방법이 있습니다. Fiber를 생성함으로써 우리는 Sketch에 무언가 비동기식으로 발생하고 있으며 스크립트를 계속 유지해야 한다고 알립니다. 그러면 Sketch는 절대적으로 필요한 경우에만 스크립트를 파괴합니다(예: 사용자가 Sketch를 닫거나 Mosaic 플러그인을 업데이트해야 할 때).

 // ... const Async = require("sketch/async"); var fiber; function onRun(context){ if(!fiber){ fiber = Async.createFiber(); fiber.onCleanup(() => { UI.cleanup(); }); } UI.loadAndShow(context.scriptURL, options => { mosaic(options); }); };

짜잔! 이제 플러그인을 사용해 보겠습니다. Sketch에서 레이어를 선택한 상태에서 몇 가지 설정을 입력한 다음 적용을 클릭합니다.

이제 플러그인을 사용해 보겠습니다. Sketch에서 레이어를 선택하고 일부 설정을 입력한 다음 "적용"을 클릭합니다.

최종 개선 사항

이제 플러그인 기능의 대부분이 구현되었으므로 약간 "축소"하고 큰 그림을 볼 수 있습니다.

사용자 경험 개선

플러그인을 현재 상태로 사용해 본 적이 있다면 Mosaic을 편집하려고 할 때 가장 큰 마찰 지점 중 하나가 나타남을 알아차렸을 것입니다. 생성한 후에는 실행 취소를 누르고 옵션을 조정한 다음 '적용'을 클릭하거나 Enter 키를 눌러야 합니다. 또한 실행 취소/다시 실행 내역이 지워지고 중복 레이어를 직접 수동으로 삭제해야 하므로 문서에서 나간 후 나중에 다시 돌아와서 모자이크를 편집하기가 더 어려워집니다.

보다 이상적인 흐름에서 사용자는 Mosaic 그룹을 선택하고 옵션을 조정하고 원하는 정확한 배열을 얻을 때까지 Mosaic 업데이트를 볼 수 있습니다. 이를 구현하기 위해 해결해야 할 두 가지 문제가 있습니다.

  1. 먼저 모자이크를 구성하는 복제본을 그룹화하는 방법이 필요합니다. Sketch는 이 문제를 해결하는 데 사용할 수 있는 Groups 개념을 제공합니다.
  2. 둘째, 일반 사용자 생성 그룹과 Mosaic 그룹을 구분할 방법이 필요합니다. 또한 Sketch의 API는 주어진 레이어에 정보를 저장할 수 있는 방법을 제공합니다. 이 정보는 길 태그로 사용하고 나중에 '특수' 모자이크 그룹 중 하나로 그룹을 식별할 수 있습니다.

이 문제를 해결하기 위해 이전 섹션에서 작성한 논리를 다시 살펴보겠습니다. 원래 코드는 다음 단계를 따릅니다.

  1. 현재 문서를 찾습니다.
  2. 현재 문서의 선택된 레이어를 찾습니다.
  3. 선택한 레이어( 템플릿 레이어라고 함)를 x 번 복제합니다.
  4. 각 복제본에 대해 사용자가 설정한 특정 값(양)으로 위치, 회전, 불투명도 등을 조정합니다.

새로운 사용자 흐름을 가능하게 하려면 다음 단계를 다음과 같이 변경해야 합니다.

  1. 현재 문서를 가져옵니다.
  2. 현재 문서의 선택된 레이어를 잡습니다.
  3. 선택한 레이어가 모자이크 그룹인지 여부를 결정합니다.
    • 다른 레이어인 경우 템플릿 레이어로 사용하고 4단계로 이동합니다.
    • Mosaic 그룹 경우 첫 번째 레이어를 템플릿 레이어로 간주하고 5단계로 이동합니다.
  4. 템플릿 레이어를 그룹 안에 감싸고 해당 그룹을 모자이크 그룹으로 표시합니다.
  5. 템플릿 레이어를 제외한 그룹 내부의 모든 레이어를 제거합니다.
  6. 템플릿 레이어를 x 번 복제합니다.
  7. 각 복제본에 대해 사용자가 설정한 특정 값으로 위치, 회전, 불투명도 등을 조정합니다.

세 가지 새로운 단계가 있습니다. 첫 번째 새 단계인 3단계에서 findOrMakeSpecialGroupIfNeeded 라는 함수를 생성합니다. 이 함수는 전달된 레이어를 보고 Mosaic 그룹인지 여부를 결정합니다. 그렇다면 그냥 돌려드리겠습니다. 사용자는 잠재적으로 Mosaic 그룹에 중첩된 하위 레이어를 선택할 수 있으므로 선택한 레이어의 상위 레이어도 확인하여 Mosaic 그룹 중 하나인지 확인해야 합니다.

 function findOrMakeSpecialGroupIfNeeded(layer){ // Loop up through the parent hierarchy, looking for a special group var layerToCheck = layer; while(layerToCheck){ if(/* TODO: is mosaic layer? */){ return layerToCheck; } layerToCheck = layerToCheck.parent; } };

Mosaic 그룹을 찾을 수 없는 경우 전달된 레이어를 Group 내부로 감싼 다음 Mosaic 그룹으로 태그를 지정합니다.

파일 맨 위로 돌아가서 이제 Group 클래스도 가져와야 합니다.

 const { Document, Group } = require("sketch/dom");
 function findOrMakeSpecialGroupIfNeeded(layer){ // Loop up through the parent hierarchy, looking for a special group var layerToCheck = layer; while(layerToCheck){ if(/* TODO: is mosaic layer? */){ return layerToCheck; } layerToCheck = layerToCheck.parent; } // Group const destinationParent = layer.parent; const group = new Group({ name: "Mosaic Group", layers: [ layer ], parent: destinationParent }); /* TODO: mark group as mosaic layer */ return group; };

이제 공백(todo's)을 채워야 합니다. 먼저 특정 집단이 우리에게 속한 특수 집단인지 아닌지를 식별할 수 있는 수단이 필요하다. 여기에서 Sketch 라이브러리의 Settings 모듈이 도움이 됩니다. 이를 사용하여 특정 레이어에 사용자 지정 정보를 저장하고 다시 읽을 수도 있습니다.

파일 맨 위에 있는 모듈을 가져오면 다음을 수행합니다.

 const Settings = require("sketch/settings");

그런 다음 setLayerSettingForKeylayerSettingForKey 제공하는 두 가지 주요 메서드를 사용하여 레이어에서 데이터를 설정하고 읽을 수 있습니다.

 function findOrMakeSpecialGroupIfNeeded(layer){ const isSpecialGroupKey = "is-mosaic-group"; // Loop up through the parent hierarchy, looking for a special group var layerToCheck = layer; while(layerToCheck){ let isSpecialGroup = Settings.layerSettingForKey(layerToCheck, isSpecialGroupKey); if(isSpecialGroup) return layerToCheck; layerToCheck = layerToCheck.parent; } // Group const destinationParent = layer.parent; layer.remove(); // explicitly remove layer from it's existing parent before adding it to group const group = new Group({ name: "Mosaic Group", layers: [ layer ], parent: destinationParent }); Settings.setLayerSettingForKey(group, isSpecialGroupKey, true); return group; };

이제 모자이크 그룹에서 레이어 래핑을 처리하는 메서드가 있으므로(또는 이미 모자이크 그룹인 경우 그냥 반환) 이제 안전 확인 직후에 이를 기본 mosaic 메서드에 연결할 수 있습니다.

 function mosaic(options){ // ... safety checks ... // Group selection if needed: const group = findOrMakeSpecialGroupIfNeeded(selectedLayer); }

다음으로 템플릿 레이어(첫 번째 레이어)를 제외한 그룹에서 모든 레이어를 제거하는 루프를 추가합니다.

 function mosaic(options) { // ... // Remove all layers except the first: while(group.layers.length > 1){ group.layers[group.layers.length - 1].remove(); } }

마지막으로 사용자가 원래 이전 그룹 내에 중첩된 레이어(제거했을 수 있는 레이어)를 선택했을 수 있으므로 그룹의 크기가 새 내용에 맞는지 확인합니다.

또한 현재 선택 항목을 모자이크 그룹 자체로 설정해야 합니다. 이렇게 하면 사용자가 동일한 모자이크 그룹을 빠르게 변경하는 경우 선택이 취소되지 않습니다. 레이어를 복제하기 위해 이미 작성한 코드 뒤에 다음을 추가합니다.

 function mosaic(options) { // ... // Fit group to duplicates group.adjustToFit(); // Set selection to the group document.selectedLayers.clear(); group.selected = true; }

플러그인을 다시 시도하십시오. 이제 모자이크 편집이 훨씬 부드러워졌습니다!

인터페이스 개선

다른 한 가지는 디스플레이 창과 그 안의 인터페이스가 동시에 표시된다는 점에서 동기화가 부족하다는 것입니다. 이는 창을 표시할 때 웹 인터페이스가 로드 완료를 보장하지 않으므로 나중에 "팝" 또는 "플래시인"되는 경우가 있기 때문입니다.

이 문제를 해결하는 한 가지 방법은 웹 인터페이스가 로드를 완료했을 때 수신 대기한 다음 창을 표시하는 것입니다. webView:didFinishNavigation: 메서드가 있으며 현재 페이지가 로드를 완료하면 WKWebView가 호출합니다. 우리는 그것을 사용하여 우리가 찾고 있는 알림을 정확히 받을 수 있습니다.

ui.js 로 돌아가서 우리가 생성한 MochaJSDelegate 인스턴스를 확장하여 이 메서드를 구현하고, 차례로 createWebView 에 전달할 onLoadFinish 인수를 호출합니다.

 function createWebView(pageURL, onApplyMessage, onLoadFinish){ const webView = WKWebView.alloc().init(); // Create delegate const delegate = new MochaJSDelegate({ "webView:didFinishNavigation:": (webView, navigation) => { onLoadFinish(); }, "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); onApplyMessage(message); } }).getClassInstance(); // Set load complete handler webView.navigationDelegate = delegate; // Set handler for messages from script const userContentController = webView.configuration().userContentController(); userContentController.addScriptMessageHandler_name(delegate, "sketchPlugin"); // Load page into web view webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent()); return webView; };

그리고 loadAndShow 메소드로 돌아가서 웹 보기가 로드된 후에만 창을 표시하도록 조정합니다.

 function loadAndShow(baseURL, onApplyMessage){ // ... const window = createWindow(); const webView = createWebView(pageURL, onApplyMessage, () => { showWindow(window); }); window.contentView = webView; _window = window; };

빙고! 이제 웹 보기가 로드를 완료한 경우에만 창을 표시하여 성가신 시각적 깜박임을 방지합니다.

결론

축하합니다. 첫 번째 Sketch 플러그인을 구축했습니다!

Mosaic을 설치하고 사용하고 싶다면 GitHub에서 전체 플러그인을 다운로드할 수 있습니다. 여행을 떠나기 전에 남은 여정 동안 유용할 수 있는 몇 가지 리소스가 있습니다.

  • developer.sketchapp.com Sketch 플러그인 개발에 관한 공식 리소스입니다. 몇 가지 유용한 가이드와 Sketch JavaScript 라이브러리에 대한 API 참조가 포함되어 있습니다.
  • Sketchplugins.com Sketch 플러그인 개발자의 환상적이고 유용한 커뮤니티입니다. 모든 불타는 질문에 대한 답변을 얻는 데 좋습니다.
  • github.com/sketchplugins/plugin-directory 공식, Sketch 플러그인의 중앙 GitHub 저장소. 여기에서 플러그인을 제출하고 나머지 Sketch 커뮤니티와 공유할 수 있습니다!