Illustratorにはオブジェクトにクリッピングマスクを適用するショートカット(command+7),解除するショートカット(command+option+7)が最初から設定されています。
それとは別に,レイヤーパネルに[クリッピングマスクを作成 / 解除]のメニューとボタンがあります。こちらはマスクの効果が対象レイヤーのサブレイヤーまで及び,階層構造を保ったまま見た目を切り抜けます。地図を作るときなどに活躍するのですが,ショートカットを設定するにはアクションを経由する必要があるため,使えるキーが少ない状態でした。
そこで今回はレイヤーのクリッピングマスクのON/OFFを切り替えるJavaScriptを紹介します。スクリプトにショートカットを割り当てるアプリとの組み合わせで,キー呼び出しを実現する作戦です。
こちらのスクリプトをダウンロードし,SPAiやKeyboard Maestroなどショートカット設定アプリに登録してください。
仕様の都合上,Illustrator CS6以降で使えます。
switchLayerClipping.jsx
使いかた
レイヤーパネルでクリッピングマスクのON/OFFを切り替えたいレイヤー選択し,スクリプトを実行してください。クリッピングがONになっている場合は解除します。OFFになっている場合は選択したレイヤーの中で最前面のパスをマスクとして使用し,クリッピングマスクをONにします。
こちらの動画はスクリプトにcontrol+command+7を割り当てて実行している見本です。
おまけ機能
基本的にクリッピングマスクのON/OFFを切り替えると,マスク枠に設定されていたアピアランス(角を丸くするなど)は消えたり要素が減ったりします。個人的にそれが面倒なことが多いので,次の条件を満たすことでマスク枠に設定されていたグラフィックスタイルを再適用する仕様にしました。
- 設定されていたグラフィックスタイル名を取得できるプラグインOnegaiSDKが使用できる
- 対象レイヤーの直下にあるパスの最初の1つをマスク枠として使う(マスク枠がサブレイヤーに入っていない状態)
- マスク枠にオーバーライドしていないグラフィックスタイルが適用してある
条件を満たさない場合は,通常の方法で切り替えたときと同じ動作です。
これでまた少し仕事が速くなりました。今日もさっさと仕事を切り上げて好きなことをしましょう!
コードはこちら
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
/** * @file レイヤークリッピングマスクのON/OFFを切り替える。 * 次の条件を満たすとマスク枠に適用されていたグラフィックスタイルを再適用する。 * - OnegaiSDKが使用できる * - マスク枠には対象レイヤーの最初のPathItemを使用している * - マスク枠にオーバーライドされていないグラフィックスタイルを使用している * @version 1.0.0 * @author sttk3.com * @copyright (c) 2019 sttk3.com */ //#target 'illustrator' (function() { var pluginName = 'OnegaiSDK' ; $.localize = true ; // Illustrator CS6未満は終了する。アクション読み込み必須なので var aiVersion = appVersion()[0] ; if(aiVersion < 16) { alert({en : 'This script requires Illustrator CS6 or later', ja : 'このスクリプトは Illustrator CS6 以降のみに対応しています。'}) ; return ; } if(app.documents.length <= 0) {return ;} var doc = app.documents[0] ; try { var origLevel = app.userInteractionLevel ; app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS ; /* マスク枠のスタイルを記録しておく。スクリプトでマスクかどうか確認できるのはPathItemとTextPathだけで グループや複合パス・複合シェイプなどはわからない。あまり複雑なことはしたくないため,最初のPathItemに 絞ってヤマを張る */ var currentLayer = doc.activeLayer ; var dstStyleName = '' ; var firstItem, origClipping ; if(enabledOf(pluginName)) { try { firstItem = currentLayer.pathItems[0] ; dstStyleName = firstItem.sendScriptMessage(pluginName, 'getGraphicStyleName', '') ; origClipping = firstItem.clipping ; } catch(e) {} } // クリッピングマスクのON/OFFトグル tempAction(actionCode(), function(actionItems) { actionItems[0].exec(false) ; }) ; // マスク枠のスタイルが取得できて,マスク状態が変化した場合のみそれを適用し直す if(dstStyleName != '' && origClipping != null && origClipping != firstItem.clipping) { doc.graphicStyles.getByName(dstStyleName).applyTo(firstItem) ; } } finally { app.userInteractionLevel = origLevel ; } })() ; /** * スクリプト実行元アプリケーションのバージョンを取得して数値の配列にする。16.0.4の場合[16, 0, 4] * @return {Array of numbers} */ function appVersion() { var tmp = app.version.toString().split('.') ; var res = [] ; for(var i = 0, len = tmp.length ; i < len ; i++) { res.push(Number(tmp[i])) ; } return res ; } /** * pluginが使用可能かどうかを返す。trueが可能 * @param {String} pluginName 対象のpluginの名前 * @return {Boolean} */ function enabledOf(pluginName) { var res = false ; try { sendScriptMessage(pluginName, '', '') ; res = true ; } catch(e) {} return res ; } function actionCode() { return '''/version 3 /name [ 13 4c61796572436c697070696e67 ] /isOpen 1 /actionCount 1 /action-1 { /name [ 4 6d616b65 ] /keyIndex 0 /colorIndex 0 /isOpen 0 /eventCount 1 /event-1 { /useRulersIn1stQuadrant 0 /internalName (ai_plugin_Layer) /localizedName [ 12 e383ace382a4e383a4e383bc ] /isOpen 1 /isOn 1 /hasDialog 0 /parameterCount 2 /parameter-1 { /key 1836411236 /showInPalette 4294967295 /type (integer) /value 18 } /parameter-2 { /key 1851878757 /showInPalette 4294967295 /type (ustring) /value [ 45 e382afe383aae38383e38394e383b3e382b0e3839ee382b9e382afe38292e4bd 9ce68890202f20e8a7a3e999a4 ] } } }''' } /** * アクションを文字列から生成し実行するブロック構文。終了時・エラー発生時の後片付けは自動 * @param {String} actionCode アクションのソースコード * @param {Function} func ブロック内処理をここに記述する * @return なし */ function tempAction(actionCode, func) { // utf8の16進数文字コードをJavaScript内部で扱える文字列に変換する var hexToString = function(hex) { var res = decodeURIComponent(hex.replace(/(.{2})/g, '%$1')) ; return res ; } ; // ActionItemのconstructor。ActionItem.exec()を使えばわざわざ名前を直接指定しなくても実行できる var ActionItem = function ActionItem(index, name, parent) { this.index = index ; this.name = name ; // actionName this.parent = parent ; // setName } ; ActionItem.prototype.exec = function(showDialog) { doScript(this.name, this.parent, showDialog) ; } ; // ActionItemsのconstructor。 // ActionItems['actionName'], ActionItems.getByName('actionName'), // ActionItems[0], ActionItems.index(-1) // などの形式で中身のアクションを取得できる var ActionItems = function ActionItems() { this.length = 0 ; } ; ActionItems.prototype.getByName = function(nameStr) { for(var i = 0, len = this.length ; i < len ; i++) { if(this[i].name == nameStr) {return this[i] ;} } } ; ActionItems.prototype.index = function(keyNumber) { var res ; if(keyNumber >= 0) { res = this[keyNumber] ; } else { res = this[this.length + keyNumber] ; } return res ; } ; // アクションセット名を取得 var regExpSetName = /^(\/name\s+\[\s+)(\d+)(\s+)([^\]]+?)(\s+\])/m ; var setName = hexToString(actionCode.match(regExpSetName)[4].replace(/\s+/g, '')) ; // セット内のアクションを取得 var regExpActionNames = /^\/action-\d+\s+\{\s+\/name\s+\[\s+\d+\s+([^\]]+?)\s+\]/mg ; var actionItemsObj = new ActionItems() ; var i = 0 ; var matchObj ; while(matchObj = regExpActionNames.exec(actionCode)) { var actionName = hexToString(matchObj[1].replace(/\s+/g, '')) ; var actionObj = new ActionItem(i, actionName, setName) ; actionItemsObj[actionName] = actionObj ; actionItemsObj[i] = actionObj ; i++ ; if(i > 1000) {break ;} // limiter } actionItemsObj.length = i ; // aiaファイルとして書き出し var failed = false ; var aiaFileObj = new File(Folder.temp + '/tempActionSet.aia') ; try { aiaFileObj.open('w') ; aiaFileObj.write(actionCode) ; } catch(e) { failed = true ; alert(e) ; return ; } finally { aiaFileObj.close() ; if(failed) {try {aiaFileObj.remove() ;} catch(e) {}} } // 同名アクションセットがあったらunloadする。これは余計なお世話かもしれない try {app.unloadAction(setName, '') ;} catch(e) {} // アクションを読み込み実行する var actionLoaded = false ; try { app.loadAction(aiaFileObj) ; actionLoaded = true ; func.call(func, actionItemsObj) ; } catch(e) { alert(e) ; } finally { // 読み込んだアクションと,そのaiaファイルを削除 if(actionLoaded) {app.unloadAction(setName, '') ;} aiaFileObj.remove() ; } hexToString = ActionItem = ActionItems = null ; } |
このサイトで配布しているスクリプトやその他のファイルを,無断で転載・配布・販売することを禁じます。
それらの使用により生じたあらゆる損害について,私どもは責任を負いません。
スクリプトやファイルのダウンロードを行った時点で,上記の規定に同意したとみなします。