これは何
VSCode上でSVGを画像編集ソフトっぽく編集したくて作っている拡張です。Marketplaceのページ。
2018/9/2 少し追記
右側のウィンドウで図形を選択して動かしたりできます。この度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;
}