抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

一、插件介绍

最近微博上在流传一个Atom编辑器的插件:activate-power-mode,装上这个插件后打字会有震屏和火花效果,非常牛逼,效果如下:
This is a picture without description

据说有人用了,并且还是机械键盘,差点被同事打断手了。

于是我花了几天的下班时间,写了个Xcode版的插件,模仿了这个效果:
image

二、下载地址

插件可以在Alcatraz上搜索ActivatePowerMode进行安装。
This is a picture without description

插件代码下载地址为:https://github.com/poboke/ActivatePowerMode

This is a picture without description

三、开发过程

这些功能实现起来也不难,主要是获取光标所在位置的代码颜色花了比较多时间。

我一开始以为代码高亮的颜色是由NSAttributedString控制的,但是我获取到的属性里只有字体字号等属性,没有NSForegroundColorAttributeName这个字段,所以只能用别的方法寻找。

用逆向思维思考一下,因为代码高亮是由配色方案管理的,切换配色方案时,代码颜色就会改变。而配色方案是根据单词的类型来设置颜色的,所以猜想可能存在某个方法,可以读取或设置某个范围的文字的颜色,这样才方便配色方案功能的实现。

先用关键字color在Xcode的私有类头文件里搜索,把搜到的方法名输出到一个文本里。然后再用关键字NSRange搜索,很快就发现了一个可疑的方法:- (id)colorAtCharacterIndex:(unsigned long long)arg1 effectiveRange:(struct _NSRange *)arg2 context:(id)arg3。然后再hook这个方法,果然返回了相应的颜色。

1、获取编辑文字时的事件

代码编辑框一般都是用NSTextView来实现的,所以要找到NSTextView的代理方法。

我之前写过一篇文章《Xcode插件AllTargets开发教程》,按照里面的方法,可以找到代码编辑区域视图的类名为IDESourceCodeEditorContainerView

该类的头文件的部分代码如下:

1
2
3
4
5
6
7
8
9
//......

@interface IDESourceCodeEditorContainerView : DVTLayoutView_ML
{
IDESourceCodeEditor *_editor;
IDEViewController *_toolbarViewController;
}

//......

IDESourceCodeEditor类里面用到了NSTextViewDelegate代理,编辑文字时会调用textView:shouldChangeTextInRange:replacementString:方法,所以可以hook这个方法。

2、如何实现震屏效果

原版的插件是用CoffeeScript写的,震屏代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
shake: ->
intensity = 1 + 2 * Math.random()
x = intensity * (if Math.random() > 0.5 then -1 else 1)
y = intensity * (if Math.random() > 0.5 then -1 else 1)

@editorElement.style.top = "#{y}px"
@editorElement.style.left = "#{x}px"

setTimeout =>
@editorElement.style.top = ""
@editorElement.style.left = ""
, 75

也就是x轴和y轴随机产生1到3像素的偏移,编辑框的原点坐标移动到这个偏移位置。
经过75毫秒后,再把编辑框的原点坐标改为(0, 0)。

在OC中可以通过修改编辑框的frame值来更改编辑框的位置,时间延迟可以使用dispatch_after方法。

3、如何实现火花粒子效果

先看一下原插件的代码:

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
spawnParticles: (range) ->
cursorOffset = @calculateCursorOffset()

{left, top} = @editor.pixelPositionForScreenPosition range
left += cursorOffset.left - @editor.getScrollLeft()
top += cursorOffset.top - @editor.getScrollTop()

color = @getColorAtPosition left, top
numParticles = 5 + Math.round(Math.random() * 10)
while numParticles--
part = @createParticle left, top, color
@particles[@particlePointer] = part
@particlePointer = (@particlePointer + 1) % 500

createParticle: (x, y, color) ->
x: x
y: y
alpha: 1
color: color
velocity:
x: -1 + Math.random() * 2
y: -3.5 + Math.random() * 2

drawParticles: ->
requestAnimationFrame @drawParticles.bind(this)
@context.clearRect 0, 0, @canvas.width, @canvas.height

for particle in @particles
continue if particle.alpha <= 0.1

particle.velocity.y += 0.075
particle.x += particle.velocity.x
particle.y += particle.velocity.y
particle.alpha *= 0.96

@context.fillStyle = "rgba(#{particle.color[4...-1]}, #{particle.alpha})"
@context.fillRect(
Math.round(particle.x - 1.5)
Math.round(particle.y - 1.5)
3, 3
)

通过spawnParticles方法随机创建5到15个粒子,保存到粒子数组里,数组上限是500个。
使用createParticle创建一个粒子,随机产生x轴和y轴的初始速度,y轴的初始速度越大,创建的粒子就跳得越高。
然后定时器每隔一段时间执行,粒子以加速度的方式下落,透明度逐渐减少。

由于我对Mac编程不太熟悉,所以使用了NSView来创建粒子,不知道有没有更好的方法。

4、获取光标所在的位置

获取光标所在的位置,以便在这个位置喷出火花,花了很多时间才找到这个方法。

可以通过rectArrayForCharacterRange:withinSelectedCharacterRange:inTextContainer:rectCount:方法来获取光标所在的位置。

评论