Help us understand the problem. What is going on with this article?

VSCodeでSVGを編集できるやつ2.0(SVG Editor)

More than 1 year has passed since last update.

これは何

VSCode上でSVGを画像編集ソフトっぽく編集したくて作っている拡張です。Marketplaceのページ。

2018/9/2 少し追記

capture.png

右側のウィンドウで図形を選択して動かしたりできます。この度Elmで書いていたのをすべてTypeScriptで書き直したので苦労話とかをまとめてみました。使って~

なぜElmをやめたの?

portが辛かった...辛かったんです...許して...

まあ実際のdom要素から取得しないといけない値とか結構あったので、だんだんきつくなっていったんですね。

はまった点

  • cssでwidthとかheightを指定するときはpxとか単位が必須、SVGと同じ気分で書いてはいけない
  • SVGの要素はdocument.createElementNSで名前空間を指定する
  • 単位換算
    • units-cssという便利ライブラリがあるがSVGにそのまま適用はできない
    • %の計算にライブラリではoffsetWidth, offsetHeightを使って要素の幅や高さを測っているがSVG要素にはそのプロパティはない...
    • あとex, emの計算がライブラリは何かおかしい
    • のでgetComputedStyleしまくる
  • viewBoxが難しい(つらい)多分負の値でまだバグがある
  • ネストされたsvg要素の当たり判定がそれの内部にある要素以上に拡大されない(ので透明のrectをこっそり挿入)
  • transform属性は頭が痛い。g要素と絡むともっと頭が痛い。
    • inkscapeはすごい
  • text要素の拡大とか
    • 「文字列の高さ(lineHeight)→フォントサイズ」を求める方法がない。逆はできる(フォントの属性を与えてlineHeightとかbaselineとかを測ってくれるライブラリはfont-measureが優秀)
    • 仕方ないので二分探索
  • 使用可能なフォントのリストが取れない(指定したフォントが使用可能かを判定することはできるらしい...: font-detect.js)。仕方ないのでOSごとにフォントが入っていそうなディレクトリを探索して(参考: system-font-families)フォントファイルのバイナリを読み込んでfontFamilyをもらってくる。始めはopentype.jsで読み込んでいたがファイルごとにすべてパースされるのが遅いので自力に切り替える(参考: ttfinfo)
  • WebViewAPIでlocalRootResourcesに許可をとってもSVGのuseタグのhrefで外部SVGリソースを参照するとはじかれる。APIのバグな気がする。htmlに埋め込むことで回避。

優秀なライブラリたち

  • font-measure フォントの属性を与えるといろいろな高さを測ってくれる。多分canvasで頑張っている。
  • incremental-dom vdomライブラリ。React、Vueは軟弱。ちゃんとSVGにも使える。
  • svgpath path要素のd属性の値をパースしていろいろいじってくれる感動ライブラリ。
  • transform-matrix transform属性のパース、行列計算のライブラリ。これも頼もしい。
  • tinycolor2 cssの色文字列を何でもパースしてくれる。

VSCodeの拡張を書いて得た知識

  • 最近できたWebview APIで色々便利になった。適用されているテーマの色をcss変数で取得できるところとか素敵。
  • untitledファイルを開く関数はvscode.workspace.openTextDocument。以下。
function workspace.openTextDocument(options?: {
    language?: string | undefined;
    content?: string | undefined;
} | undefined): Thenable<vscode.TextDocument>

おまけ Webview APIで継承されるcss変数一覧

リンクの色ってテーマで指定できないんですね。

:root {
    --background-color: #1e1e1e;
    --color: #d4d4d4;
    --font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", HelveticaNeue-Light, "Noto Sans", Meiryo, "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif;
    --font-size: 13px;
    --font-weight: normal;
    --link-active-color: #4080d0;
    --link-color: #4080d0;
    --vscode-activityBar-background: #333333;
    --vscode-activityBar-dropBackground: rgba(255, 255, 255, 0.12);
    --vscode-activityBar-foreground: #ffffff;
    --vscode-activityBarBadge-background: #007acc;
    --vscode-activityBarBadge-foreground: #ffffff;
    --vscode-badge-background: #4d4d4d;
    --vscode-badge-foreground: #ffffff;
    --vscode-button-background: #0e639c;
    --vscode-button-foreground: #ffffff;
    --vscode-button-hoverBackground: #1177bb;
    --vscode-debugExceptionWidget-background: #420b0d;
    --vscode-debugExceptionWidget-border: #a31515;
    --vscode-debugToolBar-background: #333333;
    --vscode-descriptionForeground: rgba(204, 204, 204, 0.7);
    --vscode-diffEditor-insertedTextBackground: rgba(155, 185, 85, 0.2);
    --vscode-diffEditor-removedTextBackground: rgba(255, 0, 0, 0.2);
    --vscode-dropdown-background: #3c3c3c;
    --vscode-dropdown-border: #3c3c3c;
    --vscode-dropdown-foreground: #f0f0f0;
    --vscode-editor-background: #1e1e1e;
    --vscode-editor-findMatchBackground: #515c6a;
    --vscode-editor-findMatchHighlightBackground: rgba(234, 92, 0, 0.33);
    --vscode-editor-findRangeHighlightBackground: rgba(58, 61, 65, 0.4);
    --vscode-editor-font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", HelveticaNeue-Light, "Noto Sans", Meiryo, "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif;
    --vscode-editor-font-size: 13px;
    --vscode-editor-font-weight: normal;
    --vscode-editor-foreground: #d4d4d4;
    --vscode-editor-hoverHighlightBackground: rgba(38, 79, 120, 0.25);
    --vscode-editor-inactiveSelectionBackground: #3a3d41;
    --vscode-editor-lineHighlightBorder: #282828;
    --vscode-editor-rangeHighlightBackground: rgba(255, 255, 255, 0.04);
    --vscode-editor-selectionBackground: #264f78;
    --vscode-editor-selectionHighlightBackground: rgba(173, 214, 255, 0.15);
    --vscode-editor-wordHighlightBackground: rgba(87, 87, 87, 0.72);
    --vscode-editor-wordHighlightStrongBackground: rgba(0, 73, 114, 0.72);
    --vscode-editorActiveLineNumber-foreground: #aaaaaa;
    --vscode-editorBracketMatch-background: rgba(0, 100, 0, 0.1);
    --vscode-editorBracketMatch-border: #888888;
    --vscode-editorCodeLens-foreground: #999999;
    --vscode-editorCursor-foreground: #aeafad;
    --vscode-editorError-foreground: #ea4646;
    --vscode-editorGroup-border: #444444;
    --vscode-editorGroup-dropBackground: rgba(83, 89, 93, 0.5);
    --vscode-editorGroupHeader-noTabsBackground: #1e1e1e;
    --vscode-editorGroupHeader-tabsBackground: #252526;
    --vscode-editorGutter-addedBackground: #587c0c;
    --vscode-editorGutter-background: #1e1e1e;
    --vscode-editorGutter-deletedBackground: #94151b;
    --vscode-editorGutter-modifiedBackground: #0c7d9d;
    --vscode-editorHint-foreground: rgba(238, 238, 238, 0.7);
    --vscode-editorHoverWidget-background: #2d2d30;
    --vscode-editorHoverWidget-border: #454545;
    --vscode-editorIndentGuide-activeBackground: #707070;
    --vscode-editorIndentGuide-background: #404040;
    --vscode-editorInfo-foreground: #008000;
    --vscode-editorLineNumber-activeForeground: #aaaaaa;
    --vscode-editorLineNumber-foreground: #5a5a5a;
    --vscode-editorLink-activeForeground: #4e94ce;
    --vscode-editorMarkerNavigation-background: #2d2d30;
    --vscode-editorMarkerNavigationError-background: #ea4646;
    --vscode-editorMarkerNavigationInfo-background: #008000;
    --vscode-editorMarkerNavigationWarning-background: #4d9e4d;
    --vscode-editorOverviewRuler-addedForeground: rgba(0, 122, 204, 0.6);
    --vscode-editorOverviewRuler-border: rgba(127, 127, 127, 0.3);
    --vscode-editorOverviewRuler-bracketMatchForeground: #a0a0a0;
    --vscode-editorOverviewRuler-commonContentForeground: rgba(96, 96, 96, 0.4);
    --vscode-editorOverviewRuler-currentContentForeground: rgba(64, 200, 174, 0.5);
    --vscode-editorOverviewRuler-deletedForeground: rgba(0, 122, 204, 0.6);
    --vscode-editorOverviewRuler-errorForeground: rgba(255, 18, 18, 0.7);
    --vscode-editorOverviewRuler-findMatchForeground: rgba(246, 185, 77, 0.7);
    --vscode-editorOverviewRuler-incomingContentForeground: rgba(64, 166, 255, 0.5);
    --vscode-editorOverviewRuler-infoForeground: rgba(18, 18, 136, 0.7);
    --vscode-editorOverviewRuler-modifiedForeground: rgba(0, 122, 204, 0.6);
    --vscode-editorOverviewRuler-rangeHighlightForeground: rgba(0, 122, 204, 0.6);
    --vscode-editorOverviewRuler-selectionHighlightForeground: rgba(160, 160, 160, 0.8);
    --vscode-editorOverviewRuler-warningForeground: rgba(18, 136, 18, 0.7);
    --vscode-editorOverviewRuler-wordHighlightForeground: rgba(160, 160, 160, 0.8);
    --vscode-editorOverviewRuler-wordHighlightStrongForeground: rgba(192, 160, 192, 0.8);
    --vscode-editorPane-background: #1e1e1e;
    --vscode-editorRuler-foreground: #5a5a5a;
    --vscode-editorSuggestWidget-background: #2d2d30;
    --vscode-editorSuggestWidget-border: #454545;
    --vscode-editorSuggestWidget-foreground: #d4d4d4;
    --vscode-editorSuggestWidget-highlightForeground: #0097fb;
    --vscode-editorSuggestWidget-selectedBackground: #073655;
    --vscode-editorUnnecessaryCode-opacity: rgba(0, 0, 0, 0.67);
    --vscode-editorWarning-foreground: #4d9e4d;
    --vscode-editorWhitespace-foreground: rgba(227, 228, 226, 0.16);
    --vscode-editorWidget-background: #2d2d30;
    --vscode-editorWidget-border: #454545;
    --vscode-errorForeground: #f48771;
    --vscode-extensionButton-prominentBackground: #327e36;
    --vscode-extensionButton-prominentForeground: #ffffff;
    --vscode-extensionButton-prominentHoverBackground: #28632b;
    --vscode-focusBorder: rgba(14, 99, 156, 0.6);
    --vscode-foreground: #cccccc;
    --vscode-gitDecoration-addedResourceForeground: #81b88b;
    --vscode-gitDecoration-conflictingResourceForeground: #6c6cc4;
    --vscode-gitDecoration-deletedResourceForeground: #c74e39;
    --vscode-gitDecoration-ignoredResourceForeground: #a7a8a9;
    --vscode-gitDecoration-modifiedResourceForeground: #e2c08d;
    --vscode-gitDecoration-submoduleResourceForeground: #8db9e2;
    --vscode-gitDecoration-untrackedResourceForeground: #73c991;
    --vscode-input-background: #3c3c3c;
    --vscode-input-foreground: #cccccc;
    --vscode-inputOption-activeBorder: #007acc;
    --vscode-inputValidation-errorBackground: #5a1d1d;
    --vscode-inputValidation-errorBorder: #be1100;
    --vscode-inputValidation-infoBackground: #063b49;
    --vscode-inputValidation-infoBorder: #007acc;
    --vscode-inputValidation-warningBackground: #352a05;
    --vscode-inputValidation-warningBorder: #b89500;
    --vscode-list-activeSelectionBackground: #094771;
    --vscode-list-activeSelectionForeground: #ffffff;
    --vscode-list-dropBackground: #383b3d;
    --vscode-list-errorForeground: #ea4646;
    --vscode-list-focusBackground: #073655;
    --vscode-list-highlightForeground: #0097fb;
    --vscode-list-hoverBackground: #2a2d2e;
    --vscode-list-inactiveFocusBackground: #313135;
    --vscode-list-inactiveSelectionBackground: #3f3f46;
    --vscode-list-invalidItemForeground: #b89500;
    --vscode-list-warningForeground: #4d9e4d;
    --vscode-merge-commonContentBackground: rgba(96, 96, 96, 0.16);
    --vscode-merge-commonHeaderBackground: rgba(96, 96, 96, 0.4);
    --vscode-merge-currentContentBackground: rgba(64, 200, 174, 0.2);
    --vscode-merge-currentHeaderBackground: rgba(64, 200, 174, 0.5);
    --vscode-merge-incomingContentBackground: rgba(64, 166, 255, 0.2);
    --vscode-merge-incomingHeaderBackground: rgba(64, 166, 255, 0.5);
    --vscode-notificationCenterHeader-background: #3b3b3e;
    --vscode-notificationLink-foreground: #4080d0;
    --vscode-notifications-background: #2d2d30;
    --vscode-notifications-border: #3b3b3e;
    --vscode-panel-background: #1e1e1e;
    --vscode-panel-border: rgba(128, 128, 128, 0.35);
    --vscode-panel-dropBackground: rgba(255, 255, 255, 0.12);
    --vscode-panelTitle-activeBorder: rgba(128, 128, 128, 0.35);
    --vscode-panelTitle-activeForeground: #e7e7e7;
    --vscode-panelTitle-inactiveForeground: rgba(231, 231, 231, 0.5);
    --vscode-peekView-border: #007acc;
    --vscode-peekViewEditor-background: #001f33;
    --vscode-peekViewEditor-matchHighlightBackground: rgba(255, 143, 0, 0.6);
    --vscode-peekViewEditorGutter-background: #001f33;
    --vscode-peekViewResult-background: #252526;
    --vscode-peekViewResult-fileForeground: #ffffff;
    --vscode-peekViewResult-lineForeground: #bbbbbb;
    --vscode-peekViewResult-matchHighlightBackground: rgba(234, 92, 0, 0.3);
    --vscode-peekViewResult-selectionBackground: rgba(51, 153, 255, 0.2);
    --vscode-peekViewResult-selectionForeground: #ffffff;
    --vscode-peekViewTitle-background: #1e1e1e;
    --vscode-peekViewTitleDescription-foreground: rgba(204, 204, 204, 0.7);
    --vscode-peekViewTitleLabel-foreground: #ffffff;
    --vscode-pickerGroup-border: #3f3f46;
    --vscode-pickerGroup-foreground: rgba(0, 151, 251, 0.6);
    --vscode-progressBar-background: #0e70c0;
    --vscode-scrollbar-shadow: #000000;
    --vscode-scrollbarSlider-activeBackground: rgba(191, 191, 191, 0.4);
    --vscode-scrollbarSlider-background: rgba(121, 121, 121, 0.4);
    --vscode-scrollbarSlider-hoverBackground: rgba(100, 100, 100, 0.7);
    --vscode-settings-modifiedItemForeground: #73c991;
    --vscode-sideBar-background: #252526;
    --vscode-sideBar-dropBackground: rgba(255, 255, 255, 0.12);
    --vscode-sideBarSectionHeader-background: rgba(128, 128, 128, 0.2);
    --vscode-sideBarTitle-foreground: #bbbbbb;
    --vscode-statusBar-background: #007acc;
    --vscode-statusBar-debuggingBackground: #cc6633;
    --vscode-statusBar-debuggingForeground: #ffffff;
    --vscode-statusBar-foreground: #ffffff;
    --vscode-statusBar-noFolderBackground: #68217a;
    --vscode-statusBar-noFolderForeground: #ffffff;
    --vscode-statusBarItem-activeBackground: rgba(255, 255, 255, 0.18);
    --vscode-statusBarItem-hoverBackground: rgba(255, 255, 255, 0.12);
    --vscode-statusBarItem-prominentBackground: #388a34;
    --vscode-statusBarItem-prominentHoverBackground: #369432;
    --vscode-tab-activeBackground: #1e1e1e;
    --vscode-tab-activeForeground: #ffffff;
    --vscode-tab-border: #252526;
    --vscode-tab-inactiveBackground: #2d2d2d;
    --vscode-tab-inactiveForeground: rgba(255, 255, 255, 0.5);
    --vscode-tab-unfocusedActiveForeground: rgba(255, 255, 255, 0.5);
    --vscode-tab-unfocusedInactiveForeground: rgba(255, 255, 255, 0.25);
    --vscode-terminal-ansiBlack: #000000;
    --vscode-terminal-ansiBlue: #2472c8;
    --vscode-terminal-ansiBrightBlack: #666666;
    --vscode-terminal-ansiBrightBlue: #3b8eea;
    --vscode-terminal-ansiBrightCyan: #29b8db;
    --vscode-terminal-ansiBrightGreen: #23d18b;
    --vscode-terminal-ansiBrightMagenta: #d670d6;
    --vscode-terminal-ansiBrightRed: #f14c4c;
    --vscode-terminal-ansiBrightWhite: #e5e5e5;
    --vscode-terminal-ansiBrightYellow: #f5f543;
    --vscode-terminal-ansiCyan: #11a8cd;
    --vscode-terminal-ansiGreen: #0dbc79;
    --vscode-terminal-ansiMagenta: #bc3fbc;
    --vscode-terminal-ansiRed: #cd3131;
    --vscode-terminal-ansiWhite: #e5e5e5;
    --vscode-terminal-ansiYellow: #e5e510;
    --vscode-terminal-foreground: #cccccc;
    --vscode-terminal-selectionBackground: rgba(255, 255, 255, 0.25);
    --vscode-textBlockQuote-background: rgba(127, 127, 127, 0.1);
    --vscode-textBlockQuote-border: rgba(0, 122, 204, 0.5);
    --vscode-textCodeBlock-background: rgba(10, 10, 10, 0.4);
    --vscode-textLink-activeForeground: #4080d0;
    --vscode-textLink-foreground: #4080d0;
    --vscode-textPreformat-foreground: #d7ba7d;
    --vscode-textSeparator-foreground: rgba(255, 255, 255, 0.18);
    --vscode-titleBar-activeBackground: #3c3c3c;
    --vscode-titleBar-activeForeground: #cccccc;
    --vscode-titleBar-inactiveBackground: rgba(60, 60, 60, 0.6);
    --vscode-titleBar-inactiveForeground: rgba(204, 204, 204, 0.6);
    --vscode-widget-shadow: #000000;
}
henoc
dwango
Born in the net, Connected by the net.
https://dwango.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした