Flex 用Scroller组件实现鼠标为中心的缩放
Scroller在Flex中为滚动组件,通过viewport实现数据的查看。
有关如何使用Scroller实现大量数据展示的文章可参见http://www.adobe.com/devnet/flex/articles/flex4_viewport_scrolling.html。本篇文章主要解决如何通过鼠标滚轮实现缩放功能(以鼠标位置为中心),主要是遇到了问题,才写这篇文章,希望可以帮到看
在Flex中实现缩放本身很容易,通过设置Scroller的viewport组件的scaleX和scaleY实现内容的缩放。
(图片参考至Adobe网站)
问题在于如果需要实现鼠标为中心的缩放就比较麻烦,需要依据鼠标的位置,还有视口的位置,来实现放大,网上大部分文章都是图片的缩放,相对而言比较简单。笔者在实现设计器的时候,需要显示的内容比较多,当然也是存在一个Group中的,关键在于鼠标事件中调整视口的位置时,会在后续事件中有些许变化,所以难点在于在什么时间处理视口位置的问题(顺便提一下:Flex中事件处理的时机很难懂,真的很难)。
下面简单介绍一下以鼠标为中心的缩放方式:
- 获得鼠标在内容的位置cx,cy和在视口的位置wx,wy
- 设置新的缩放比率,通过检查鼠标滚轮事件的变化值设置scaleX, scaleY
- 计算视口左上角的位置:cx-wx/scaleX, cy-wy/scaleY
- 保存视口左上角位置
- 在组件的Resize事件中设置verticalScrollPosition, horizantialScrollPosition值为新值
该过程关键在于不能直接在whell事件中设置滚动条的值,原因如下:
在scaleX/Y函数中,会调用invalidateProperties()和invalidateSize()函数,以重新计算视口的宽和高(视口的宽和高会随缩放比率而变化),如果直接在whell事件中处理是,由于后期的处理会导致位置有些偏移,因此应该在组件内部完成计算后在计算新的位置才可以保证不会变化,实现精准的以鼠标为中心的缩放定位。
具体代码就简单了。
在视口组件中实现鼠标滚动时间(MOUSE_WHEEL)和Resize(RESIZE)事件。
public function xxxx():void{
// 添加事件
addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
addEventListener(Event.RESIZE, viewportResizeHandler);
}
private var newPositionX:Number;
private var newPositionY:Number;
private var updatePosition:Boolean = false;
protected function viewportResizeHandler(event:ResizeEvent):void{
if (updatePosition){
horizontalScrollPosition = newPositionX;
verticalScrollPosition = newPositionY;
if (horizontalScrollPosition + width > this.contentWidth){
horizontalScrollPosition = this.contentWidth - width;
}
if (verticalScrollPosition + height > this.contentHeight){
verticalScrollPosition = this.contentHeight - height;
}
if (horizontalScrollPosition < 0){
horizontalScrollPosition = 0;
}
if (verticalScrollPosition < 0){
verticalScrollPosition = 0;
}
updatePosition = false;
}
}
protected function mouseWheelHandler(event:MouseEvent):void{
var ptGlobal:Point;
var ptLocal:Point;
var parentPT:Point;
ptGlobal = new Point(event.stageX, event.stageY);
parentPT = parent.parent.globalToLocal(ptGlobal);
ptLocal = globalToLocal(ptGlobal);
if (event.delta > 0){
this.scaleX *= 2;
this.scaleY *= 2;
//!!! 滚动条的调整必须在Resize事件之后处理
//!!! 由于滚动条在scale之后会有变化,不然会存在后期调整导致缩放不能以虚标为中心缩放
newPositionX = ptLocal.x - parentPT.x/scaleX;
newPositionY = ptLocal.y - parentPT.y/scaleY;
}else{
this.scaleX /= 2;
this.scaleY /= 2;
//!!! 滚动条的调整必须在Resize事件之后处理
newPositionX = ptLocal.x - parentPT.x/scaleX;
newPositionY = ptLocal.y - parentPT.y/scaleY;
}
updatePosition = true;
}