先说一下,网站开启了不需要注册登录就可以回复的模式,只需要写一下姓名和邮箱就可以了。所以如果有反馈现在可以很方便的直接回复了。
这个脚本巨难,反正按着我现在不入流的水准来看没有个十天半个月是搞不懂了。
主要是使用了 this 和对象之间的互相调用,逻辑上比较绕,看了一周左右,勉强懂个七七八八。所以看之前最好先搜索以下 this 的用法。
最开始研究这个脚本是因为想学习下怎么让脚本中文字变大,结果发现那个效果并不能在Ae中实现。
脚本中也大量运用了ScriptUI Path对象等等,如果不先看一下http://worldcter.com/1012.html中的内容,根本没办法理解。
所以总的来看,这个脚本对我现在需要的内容帮不上忙,而且研究了十天了,耽误了太长时间,就先到此结束吧。
先把这个半成品分析发上来,以后水平上来了理解起来就应该会容易些了吧。
对了,为了帮助理解,我还画了一个对象方法之间的思维导图,有需要的可以点击开启大图模式,直接拖拽到地址栏中就可以保存了。
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 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
/** * 这段脚本创建了一个颜色选取器浮动面板,来演示怎样使用 ScriptUI 的一些图形属性和方法 * 主要展示了以下: * 定义一个 ScriptUIFont 对象,使用这个对象来设置元素而 graphics.font 属性 * 定义一个 ScriptUIBrush 对象,使用这个对象来设置元素的 graphics.background 属性 * 定义一个 onDraw 事件处理函数给 UI 元素,并使用不同方式来绘制元素 * 定义一个分割线和矩形路径 * 使用动态 RGB 颜色创建的 ScriptUIBrush 填充路径 * 使用静态颜色创建的 ScriptUIPen 对象绘制路径(为一些钢笔对象使用局部透明) */ function ColorPicker() {} /** * 这段案例的函数部分,创建几个新类型的节点和一个管理对象 * 管理对象储存着节点标识前缀和根节点 */ ColorPicker.prototype.run = function() { // 定义常量 /** * 要使用的字体的大小 * @type 数字 */ var kFontSize = 90; /** * 线宽,像素单位,用于主颜色板 * @type 数字 */ var kSwatchBorderWidth = 2; /** * RGBPickerComponent 类实现了 RGBColorPicker 类的单个颜色组件 * 它为用户提供了使用 Slider 和 EditText 元素控制单个 RGB 组件颜色的阶度 */ function RGBPickerComponent(componentIndex) { // 定义单个调节颜色组的 UI 元素和布局 // 这里的 this 只是先定义了,具体确定指向要看调用时的上一级对象 this.uiResource = "group {\ spacing: 2,\ alignChildren: ['right', 'center'],\ swatch: Panel {\ alignment: ['left', 'center'],\ properties: {borderStyle: 'sunken'},\ preferredSize: [18,18]\ },\ label: StaticText {},\ slColor: Slider {\ minvalue: 0, maxvalue: 100\ },\ etColor: EditText {\ characters: 3, justify: 'right'\ },\ percent: StaticText {text: '%'}\ }"; // 在后面外部定义了 initRGBPickerComponent() // 相当于在 RGBPickerComponent() 中先定义了一个方法,然后又调用了一次,不过是使用 this 调用的 // initRGBPickerComponent() 中定义了 this.rgbVal 和 this.componentIndex // 因为 this 指向调用函数的那个对象,所以在下面的这个调用中,实际上 this 指向了 引用的这个函数的 this // 即:在 RGBPickerComponent() 对象中又定义了一个 this.rgbVal 和 this.componentIndex // 不过这回的 this 指向的是实例化或调用 RGBPickerComponent() 的那个对象 // 至于为什么要这样绕一圈去定义两个属性,是为了可重用性,总得来说,下面的引用可以换成下面两行代码 // this.rgbVal = [0,0,0]; // this.componentIndex = componentIndex; this.initRGBPickerComponent(componentIndex); } // 初始化 RGB 选择器组件 // 为 RGBPickerComponent 对象添加一个方法 RGBPickerComponent.prototype.initRGBPickerComponent = function(componentIndex) { // 红绿蓝颜色都为0,初始值为黑色,没有包含 Alpha this.rgbVal = [0,0,0]; this.componentIndex = componentIndex; }; // 初始化 UI 控件参数 RGBPickerComponent.prototype.initialize = function(pickerObj, colorLabel, initialValue) { this.pickerObject = pickerObj; // getContainer() 是后面定义的一个方法 // RGBColorPicker.prototype.getContainer(),返回的是引用对象的容器对象 // 所以如果想让下面的代码不出错,pickerObj 必须是 RGBColorPicker 对象或是其派生的对象 // 作用是给容器添加一个资源字符串定义 UI ,返回一个 Group 对象 this.ui = pickerObj.getContainer().add(this.uiResource); // 将 UI 对象连接回这个对象 // 返回 Group 对象(this.ui)的 rgbComponentObj 属性值为引用该方法的对象 // 以后使用 this.ui.rgbComponentObj 等同于调用 this // 但是使用 this 的时候还是 this this.ui.rgbComponentObj = this; // with 的意思是下面大括号内的所有属性前都自动加上 this.ui with(this.ui) { // 这里的 label 和 etColor 为控件的名称 // 在上面的资源字符串中可以找到 // 一般情况下会命名一个变量,这里就使用名称代替该控件了 // getUIFont() 也是后面定义的,这里引用 getUIFont() 的还是 pickerObj,与上面一致 label.graphics.font = pickerObj.getUIFont(); label.text = colorLabel; slColor.value = initialValue * slColor.maxvalue; slColor.onChanging = slColor.onChange = function() { // this 为滑动条元素,因为这个是 onChange() 函数内部的this // 而引用 onChange() 的是slColor,是滑动条控件的名称 // 让可编辑文本的数值为滑动条值 this.parent.etColor.text = this.value; this.parent.rgbComponentObj.updateSwatch(); }; etColor.graphics.font = pickerObj.getUIFont(); // 这个 onChange 事件响应函数赋予“通过改变可编辑文本来改变颜色”的功能 etColor.onChange = function() { // this 在这里是 edittext 元素 // 限制它的值在滑动条最小值和最大值之间 // slider 代表着滑动条 var slider = this.parent.slColor; // this.text 指向可编辑文本的数值 var val = Number(this.text); if (val > slider.maxvalue) {val = slider.maxvalue;} else if (val < slider.minvalue) {val = slider.minvalue;} slider.value = val; this.text = val; // updateSwatch() 也是下面定义的 this.parent.rgbComponentObj.updateSwatch(); }; } // with 结束 // 下面这句代码相当于 // this.ui.slColor.value = initialValue * this.ui.slColor.maxvalue; // this.ui.slColor.notify("onChange"); this.setComponentValue(initialValue); }; // 接着给 RGBPickerComponent() 对象添加方法 RGBPickerComponent.prototype.getComponentValue = function() { // 返回 this.rgbVal[index] 要处理的颜色通道数组 // 最开始设置的 rgbVal 属性值为三个数组,分别是红绿蓝 // 这里的 this.componentIndex 是颜色索引号 return this.rgbVal[this.componentIndex]; }; RGBPickerComponent.prototype.setComponentValue = function(colorVal) { // 滑动条的值为 初始化值 乘以 滑动条最大值(100) this.ui.slColor.value = colorVal * this.ui.slColor.maxvalue; // notify() 是 ExtendScript 定义的一个函数,可以立即调用控件的事件响应函数 // 具体可以下载 worldcter.com 中的《ExtendScript工具参考手册》查阅 this.ui.slColor.notify("onChange"); }; // 更新单独颜色通道的颜色板 RGBPickerComponent.prototype.updateSwatch = function() { this.rgbVal[this.componentIndex] = this.ui.slColor.value / this.ui.slColor.maxvalue; var uiGfx = this.ui.graphics; this.ui.swatch.graphics.backgroundColor = uiGfx.newBrush(uiGfx.BrushType.SOLID_COLOR, this.rgbVal); this.ui.swatch.graphics.disableBackgroundColor = this.ui.swatch.graphics.backgroundColor; // 这是在移动灰色滑动条时修改红绿蓝三个颜色通道的颜色板 this.pickerObject.updateRGBSwatch(); }; /** * GrayPicker 类是从 RGBPickerComponent 类派生的 * 它允许用户选择灰色着色器同时改变红、绿和蓝色组件值 * 与用户改变单个 Slider 或 EditText 域相同 * 它有一个隐藏的颜色板,因为我们不想预览灰色着色器 * 主选择颜色板会显示 */ function GrayPicker() {this.initGrayPicker();} // 由 RGBPickerComponent 派生,“重写”它的初始化(initialize)方法 // GrayPicker 共享了 RGBPickerComponent 的全部属性和方法,不过在调用的时候 componentIndex 为 0 GrayPicker.prototype = new RGBPickerComponent(0); // 这里将 initialize 方法传递给 initializeRGBPickerComponent // 后面重写 initialize 时,原本的逻辑代码仍然保存在 initializeRGBPickerComponent GrayPicker.prototype.initializeRGBPickerComponent = GrayPicker.prototype.initialize; // initRGBPickerComponent() 是上面 RGBPickerComponent 对象定义的,返回的是 // this.rgbVal = [0,0,0]; // this.componentIndex = 0; // GrayPicker() 在创建时就调用了一次 initGrayPicker() GrayPicker.prototype.initGrayPicker = function() {this.initRGBPickerComponent(0);}; // 这里重写了 initialize(),基本都一样,只不过添加了一个 visible 属性为 false,让灰色颜色板不可见 GrayPicker.prototype.initialize = function(pickerObj, colorLabel, initialGrayLevel) { this.initializeRGBPickerComponent(pickerObj, colorLabel, initialGrayLevel); this.ui.swatch.visible = false; }; // 将所有色板更新为所选的灰色着色器,这也是重写的 GrayPicker.prototype.updateSwatch = function() { var grayLevel = this.ui.slColor.value / this.ui.slColor.maxvalue; this.pickerObject.enableUpdates(false); this.pickerObject.redPicker.setComponentValue(grayLevel); this.pickerObject.greenPicker.setComponentValue(grayLevel); this.pickerObject.bluePicker.setComponentValue(grayLevel); this.pickerObject.enableUpdates(true); this.pickerObject.updateRGBSwatch(); }; /** * RGBColorPicker 类使用 GrayPicker 实例和 RGBPickerComponent 实例创建 RGB选择器UI * 在 ColorPickerWin 对象中实例化了这个对象: * colorPickerWin.rgbPicker = new RGBColorPicker(colorPickerWin.rgbPicker, [1,1,1], kFontSize); */ function RGBColorPicker(container, initialRGB, fontSize) { this.initRGBColorPicker(container, initialRGB, fontSize); } /** * 初始化默认值,设置组容器的定向并添加 RGBPickerComponents */ RGBColorPicker.prototype.initRGBColorPicker = function(container, initialRGB, fontSize) { this.container = container; this.rgbValue = initialRGB; // Script 类方法:ScriptUI.newFont(name,style,size) // 特别遗憾的是,在 AE 中修改字体大小无效! this.uiFont = ScriptUI.newFont("palette", undefined, fontSize); this.updatesEnabled = false; // 为每个 RGB 组件颜色创建一个选择器 UI container.orientation = "column"; container.alignChildren = ["fill", "top"]; container.spacing = 5; // 实例化 RGBPickerComponent(),并立即调用 initRGBPickerComponent() 方法 // this.redPicker.rgbVal = [0,0,0]; // this.redPicker.componentIndex = 0; this.redPicker = new RGBPickerComponent(0); this.greenPicker = new RGBPickerComponent(1); this.bluePicker = new RGBPickerComponent(2); this.enableUpdates(false); // 这里的 initialize() 方法是 RGBPickerComponent() 对象的方法,相当于: // this.pickerObject = this; // this.ui = this.getContainer().add(this.uiResource); // this.ui.rgbComponentObj = this; // with(this.ui) { // label.graphics.font = this.getUIFont(); // label.text = "红:"; // slColor.value = initialRGB[0] * slColor.maxvalue; // slColor.onChanging = slColor.onChange = function() { // this.parent.etColor.text = this.value; // this.parent.rgbComponentObj.updateSwatch(); // } // etColor.graphics.font = this.getUIFont(); // etColor.onChange = function() { // var slider = this.parent.slColor; // var val = Number(this.text); // if (val > slider.maxvalue) {val = slider.maxvalue;} // else if (val < slider.minvalue) {val = slider.minvalue;} // slider.value = val; // this.text = val; // this.parent.rgbComponentObj.updateSwatch(); // } // } // this.setComponentValue(initialRGB[0]); this.redPicker.initialize(this, "红:", initialRGB[0]); this.greenPicker.initialize(this, "绿:", initialRGB[1]); this.bluePicker.initialize(this, "蓝:", initialRGB[2]); this.enableUpdates(true); // 同样的,实例化 GrayPicker() 并立即调用它的初始化方法 this.grayPicker = new GrayPicker(); this.grayPicker.initialize(this, "灰度:", 1); this.updateRGBSwatch(); }; RGBColorPicker.prototype.getContainer = function() { return this.container; }; RGBColorPicker.prototype.getUIFont = function() { return this.uiFont; }; RGBColorPicker.prototype.getRGBColor = function() { return this.rgbValue; }; RGBColorPicker.prototype.enableUpdates = function(enable) { this.updatesEnabled = enable; }; RGBColorPicker.prototype.updateRGBSwatch = function() { if (this.updatesEnabled) { this.rgbValue[0] = this.redPicker.getComponentValue(); this.rgbValue[1] = this.greenPicker.getComponentValue(); this.rgbValue[2] = this.bluePicker.getComponentValue(); var swatchGfx = this.container.window.rgbSwatch.graphics; swatchGfx.backgroundColor = swatchGfx.newBrush(swatchGfx.BrushType.SOLID_COLOR, this.rgbValue); swatchGfx.disableBackgroundColor = swatchGfx.backgroundColor; this.container.window.rgbSwatch.btn.bgPen = swatchGfx.newPen(swatchGfx.PenType.SOLID_COLOR, this.rgbValue, kSwatchBorderWidth); } }; // 创建颜色选择器窗口 // 这段资源字符串创建的是面板和显示最终颜色的大按钮和容纳滑动条的组 // 注意这里面板的创造参数中有一个 closeOnKey 来创建一个关闭窗口的快捷键 var colorPickerRes = "palette {\ text: '颜色选择器',\ properties: {closeOnKey:'OSCmnd+R', independent:true},\ orientation: 'row',\ rgbSwatch: Group {\ btn: Button {preferredSize:[40,40]}\ },\ rgbPicker: Group {}\ }"; // 其实可以从这个对象开始撸起,看看都实例化了哪些对象 var colorPickerWin = new Window(colorPickerRes); // 创建那三个滑动条和颜色板 colorPickerWin.rgbPicker = new RGBColorPicker(colorPickerWin.rgbPicker, [1,1,1], kFontSize); // 为那个显示颜色的大按钮创建两个事件响应函数 colorPickerWin.rgbSwatch.btn.onDraw = drawRGBSwatch; colorPickerWin.rgbSwatch.btn.onClick = clickRGBSwatch; /** * 强制初始布局,所以UI元素的尺寸是已知的 * 并强制色板组紧贴按钮(覆盖布局管理器的默认值) */ // winObj.layout.layout() 强制布局管理器为容器和任一子容器重新计算大小 colorPickerWin.layout.layout(true); with (colorPickerWin.rgbSwatch) { size = btn.size; location = [btn.windowBounds.x, btn.windowBounds.y]; btn.location = [0,0]; } initializeDrawingState(colorPickerWin.rgbSwatch.btn); // 现在显示窗口 colorPickerWin.show(); /** * RGB sawtch 用来实现组元素和一个完全填充的按钮 * 我们使用组是因为组的 graphics.backgroundColor 可以设置 * 所以我们可以设置为当前选中的 RGB 颜色 * 我们将按钮放进这个组中来演示使颜色板“激活” * 并展示一个使按钮从根本上变为透明的技术 * 这主要是通过一个 onDraw 事件处理函数控制不绘制按钮的 background * 只保留边界 * 我们初始化 rgb swatch 按钮的“静态”绘制环境,具体如下: * “边界”路径是常量,所以立即创建而不是每次绘制颜色板时 * 同样的,边界的钢笔对象的颜色也是常量,只需要创建一次 */ function initializeDrawingState(swatchBtn) { // swatchBtn 为一个按钮控件对象 var gfx = swatchBtn.graphics; var btnW = swatchBtn.size.width; var btnH = swatchBtn.size.height; // 定义左上和右下的边界路径 // kSwatchBorderWidth 是在 run() 中定义的 // 就在脚本的最前面,实际上一直到最后一个大括号前都属于 run() 内部操作 var halfBorderW = kSwatchBorderWidth / 2; // controlObj.graphics.newPath() // 在currentPath中创建一个新的、空的绘制路径,代替任何已存在的路径。返回ScriptUIPath对象 gfx.newPath(); // moveTo() 和 lineTo() 比较复杂,下载《ExtendScript工具参考手册》看下 ScriptUIPath 对象先吧 gfx.moveTo(halfBorderW, btnH - halfBorderW); gfx.lineTo(halfBorderW, halfBorderW); gfx.lineTo(btnW - halfBorderW, halfBorderW); swatchBtn.tlBorderPath = gfx.currentPath; gfx.newPath(); gfx.moveTo(halfBorderW, btnH - halfBorderW); gfx.lineTo(btnW - halfBorderW, btnH - halfBorderW); gfx.lineTo(btnW - halfBorderW, halfBorderW); swatchBtn.brBorderPath = gfx.currentPath; // 定义边界钢笔对象:使用半透明的笔,这样背景色就会显示出来 swatchBtn.highlightPen = gfx.newPen(gfx.PenType.SOLID_COLOR, [1,1,1,0.4], kSwatchBorderWidth); swatchBtn.shadowPen = gfx.newPen(gfx.PenType.SOLID_COLOR, [0.25,0.25,0.25,0.4], kSwatchBorderWidth); } /** * 这是 rgb 颜色板按钮的 onDraw 事件处理函数 * 因为这个函数会在每次按钮绘制时调用,所以我们想让它尽可能的快速执行 * 因此这个函数的绘制状态尽可能的使用“外部”派生的 */ function drawRGBSwatch(drawingStateObj) { var gfx = this.graphics; try { /** * 不要绘制按钮背景,通过我们的容器背景颜色显示,只需要绘制边界 * 基于鼠标状态:第一个使用容器的固态背景色绘制边界的下部分 * 之后使用半透明边界高亮和阴影绘制顶部 */ gfx.strokePath(this.bgPen, this.tlBorderPath); gfx.strokePath(this.bgPen, this.brBorderPath); if (drawingStateObj.leftButtonPressed) { gfx.strokePath(this.shadowPen, this.tlBorderPath); gfx.strokePath(this.highlightPen, this.brBorderPath); } else { gfx.strokePath(this.highlightPen, this.tlBorderPath); gfx.strokePath(this.shadowPen, this.brBorderPath); } } catch (e) { // 出现任何问题,就使 onDraw 事件处理函数未定义,这样我们就不用在返回这里了 this.onDraw = undefined; alert("drawRGBSwatch 事件处理函数失败。\n" + e); } } // 这是为 rgb 颜色板按钮注册的 onClick 事件处理函数 function clickRGBSwatch() { var rgb = this.window.rgbPicker.getRGBColor(); alert("选中的 RGB 颜色:\n" + rgb[0] * 100 + "%红色\n" + rgb[1] * 100 + "%绿色\n" + rgb[2] * 100 + "%蓝色"); } }; new ColorPicker().run(); |