lua 自定义控件及动画控制(1) ---- 实现自定义滑动控件

1 lua控件基类

_class = {}
local UIBase = class("UIBase",cc.Node)
function UIBase:ctor()  
    -- 触摸监听事件  
    local listener = cc.EventListenerTouchOneByOne:create()  
    listener:registerScriptHandler(function ( touch,event )  
        return self:onPanelTouchBegan(touch,event)  
    end,cc.Handler.EVENT_TOUCH_BEGAN)  
    listener:registerScriptHandler(function ( touch,event )  
        return self:onPanelTouchMoved(touch,event)  
    end,cc.Handler.EVENT_TOUCH_MOVED)  
    listener:registerScriptHandler(function ( touch,event )  
        return self:onPanelTouchEnded(touch,event)  
    end,cc.Handler.EVENT_TOUCH_ENDED)  
    self.listener = listener  
  
    local eventDispatch = self:getEventDispatcher()  
    eventDispatch:addEventListenerWithSceneGraphPriority(listener,self) -- self:响应事件的对象 

    -- script handler
    local function onNodeEvent(event)
        if "enter" == event then
            self:onEnter()
        elseif "exit" == event then
            self:onExit()
        end
    end

    self:registerScriptHandler(onNodeEvent)
end

function UIBase.isTouchInside(node,touch)
	local touchLoc = touch:getLocation()
	touchLoc = node:getParent():convertToNodeSpace(touchLoc)
	if not cc.rectContainsPoint(node:getBoundingBox(), touchLoc) then
	    return false
	end
	return true
end

function UIBase:onEnter()
end
function UIBase:onExit()
end

function UIBase:onPanelTouchBegan( touch,event )  
    -- body  
end  
function UIBase:onPanelTouchMoved( touch,event )  
    -- body  
end  
function UIBase:onPanelTouchEnded( touch,event )  
    -- body  
end 
_class.UIBase = UIBase

2 各种自定义滑动选择控件

2.1 类型1

效果


采用的曲线方程

lua源码 参考(http://blog.csdn.net/ccy0815ccy/article/details/43924895)

local UIRotateMenu1 = class("UIRotateMenu1",_class.UIBase)

UIRotateMenu1.AniDuration = 0.5 -- 运动延时
UIRotateMenu1.Factror = 2 -- 控制曲线形态
UIRotateMenu1.ItemScrollFactor = 1/5 -- 滑过1/5宽度对应滑动过一个item
UIRotateMenu1.IndexScrollFactor = 1/3 -- 滑动超过1/3自动转至下个Index
function UIRotateMenu1:ctor()
	_class.UIBase.ctor(self)
	self._itemT = {}
	self._index = 0 -- 中心位置Item index
	self._lastindex = 0
    local cs = cc.Director:getInstance():getWinSize()
    self:setContentSize(cs)
    -- self:setContentSize(cc.size(cs.width*2/3,cs.height*2/3))
end

function UIRotateMenu1:title()
 	return "Moke And TianTian"
end

function UIRotateMenu1:addMenuItem(item)
	local cs = self:getContentSize()
	item:setPosition(cc.p(cs.width/2,cs.height/2))
	self:addChild(item)
	table.insert(self._itemT,item)
	self:reset()
	self:updatePosWithAni()
end

function UIRotateMenu1:reset()
	self._index = 0
	self._lastindex = 0
end
-- 矫正中心Index
function UIRotateMenu1:rectify( deltaX )
	local isforward = nil
	if deltaX then
		if deltaX > 0 then 
			isforward = true
		else
			isforward = false
		end
	end
	local idx = self:_getIndex()
	if idx < 0 then
		idx = idx + #self._itemT
	elseif idx > (#self._itemT - 1) then
		idx = idx - #self._itemT
	end
	if isforward == true then
		idx = math.floor(idx + self.IndexScrollFactor)		
	elseif isforward == false then
		idx = math.floor(idx + 2*self.IndexScrollFactor)
	end
	self:_setIndex(idx)
end

function UIRotateMenu1:updatePos()
	for k,v in pairs(self._itemT) do
		v:setPosition(self:_getPos(k-1))
		v:setScale(self:_getScale(k-1))
		v:setRotation3D(cc.vec3(0,self:_getRotation(k-1),0))
		v:setLocalZOrder(self:_getZorder(k-1))
	end
end
function UIRotateMenu1:updatePosWithAni()
	for k,v in pairs(self._itemT) do
		v:stopAllActions()
	end
	for k,v in pairs(self._itemT) do
		local newpos = self:_getPos(k-1)
		v:moveTo({time = self.AniDuration, x = newpos.x, y = newpos.y})
		v:scaleTo({time = self.AniDuration, scale = self:_getScale(k-1)})
		v:rotateTo({time = self.AniDuration, rotation = cc.vec3(0,self:_getRotation(k-1),0)})
		v:setLocalZOrder(self:_getZorder(k-1))
	end
	local delayTimeAct = cc.DelayTime:create(self.AniDuration)
	local sequence = cc.Sequence:create(delayTimeAct,cc.CallFunc:create(handler(self,self.callFunc)))
	self:runAction(sequence)
end

function UIRotateMenu1:_getPos(idx)
	local cs = self:getContentSize()
	local deltaX = self:curveFunc(self:transIdxToCurveFuncVar(idx),cs.width/2)
	return cc.p(cs.width/2 + deltaX,cs.height/2)
end
function UIRotateMenu1:_getZorder(idx)
	return -math.abs(idx-self:_getIndex())
end
function UIRotateMenu1:_getScale(idx)
	return 1- math.abs(self:curveFunc(self:transIdxToCurveFuncVar(idx),1))
end
function UIRotateMenu1:_getRotation(idx)
	return self:curveFunc(self:transIdxToCurveFuncVar(idx),60)
end
function UIRotateMenu1:_getIndex()
	return self._index
end
function UIRotateMenu1:_setIndex( curTmpIdx )
	self._index = curTmpIdx
end

function UIRotateMenu1:callFunc()
	self:rectify()
	local curSp = self._itemT[self:_getIndex() + 1]
	if curSp then
		self.label:setString(curSp:getName())
	end
end

function UIRotateMenu1:onPanelTouchBegan( touch,event )  
    -- cclog("UIRotateMenu1:onPanelTouchBegan")
    if not self.isTouchInside(self,touch) then
    	return false
    end
    for k,v in pairs(self._itemT) do
    	v:stopAllActions()
    end
    return true
end 
function UIRotateMenu1:onPanelTouchMoved( touch,event )
	local deltaX = touch:getDelta().x
	local curTmpIdx = self:transMoveLenToVar(deltaX)
	self:_setIndex(curTmpIdx)
	self:updatePos()
end
function UIRotateMenu1:onPanelTouchEnded( touch,event )
	local deltaX = touch:getLocation().x - touch:getStartLocation().x
	self:rectify(deltaX)
	self:updatePosWithAni()
end

-- 将移动长度转为核心变量
function UIRotateMenu1:transMoveLenToVar(moveLen)
	local cs = self:getContentSize()
	return self._index - moveLen/(cs.width * self.ItemScrollFactor)
end
-- 将当前Idx转为曲线方程自变量
function UIRotateMenu1:transIdxToCurveFuncVar(idx)
	return idx - self:_getIndex() 
end
function UIRotateMenu1:curveFunc(x,width) -- x:deltaIdx
	return width * x/(math.abs(x)+self.Factror)
end


function UIRotateMenu1:onEnter()
	cclog("UIRotateMenu1:onEnter")
	-- title
    local label = cc.Label:createWithTTF(self.title(), "fonts/arial.ttf", 32)
    self:addChild(label)
    label:setAnchorPoint(cc.p(0.5, 0.5))
    label:setPosition( cc.p(VisibleRect:center().x, VisibleRect:top().y - 50) )

    -- index label
    label = cc.Label:createWithTTF("", "fonts/arial.ttf", 22)
    label:setColor(cc.c3b(255,0,0))
    self:addChild(label)
    label:setAnchorPoint(cc.p(0.5, 0.5))
    label:setPosition( cc.p(VisibleRect:center().x, VisibleRect:top().y - 80) )
    self.label = label

    local ball = cc.Sprite:create("Images/moke1.jpg")
    ball:setName("moke1")
    self:addMenuItem(ball)
    ball = cc.Sprite:create("Images/moke2.jpg")
    ball:setName("moke2")
    self:addMenuItem(ball)
    ball = cc.Sprite:create("Images/moke3.jpg")
    ball:setName("moke3")
    self:addMenuItem(ball)
    ball = cc.Sprite:create("Images/moke4.jpg")
    ball:setName("moke4")
    self:addMenuItem(ball)
    ball = cc.Sprite:create("Images/moke5.jpg")
    ball:setName("moke5")
    self:addMenuItem(ball)
    -- ball = cc.Sprite:create("Images/moke6.jpg")
    -- self:addMenuItem(ball)
end
function UIRotateMenu1:onExit()
end


function UIRotateMenu1Main()
	cclog("UIRotateMenu1Main")
	local scene = cc.Scene:create()
	local rotateMenu = UIRotateMenu1:create()
	scene:addChild(rotateMenu)
	scene:addChild(CreateBackMenuItem())
	return scene
end

2.2 类型2

效果


函数方程



Lua源码( 参:http://blog.csdn.net/ccy0815ccy/article/details/41519767 )
local UIRotateMenu2 = class("UIRotateMenu2",_class.UIBase)

UIRotateMenu2.AniDuration = 0.5 -- 运动延时
UIRotateMenu2.Factror = 2 -- 控制曲线形态
UIRotateMenu2.ItemScrollFactor = 1/5 -- 滑过1/5宽度对应滑动过一个item
UIRotateMenu2.IndexScrollFactor = 1/3 -- 滑动超过1/3自动转至下个Index
function UIRotateMenu2:ctor()
	_class.UIBase.ctor(self)
	self._itemT = {}
	self._index = 0 -- 中心位置Item index
	self._unitAngle = 0 -- 两个item间相隔的离心角(单位离心角)
    local cs = cc.Director:getInstance():getWinSize()
    self:setContentSize(cs)
    -- self:setContentSize(cc.size(cs.width*2/3,cs.height*2/3))
    self.majorSemiAxis = cs.width/4 -- 长半轴
    self.minorSemiAxis = cs.height/4 -- 短半轴
end

function UIRotateMenu2:title()
 	return "Moke And TianTian"
end

function UIRotateMenu2:addMenuItem(item)
	local cs = self:getContentSize()
	item:setPosition(cc.p(cs.width/2,cs.height/2))
	self:addChild(item)
	table.insert(self._itemT,item)
	self:reset()
	self:updatePosWithAni()
end

function UIRotateMenu2:reset()
	self._index = 0
	self._unitAngle = 2 * math.pi / #self._itemT
end
function UIRotateMenu2:rectify( deltaX )
	local isforward = nil
	if deltaX then
		if deltaX > 0 then 
			isforward = true
		else
			isforward = false
		end
	end
	local idx = self:_getIndex()
	if idx < 0 then
		idx = idx + #self._itemT
	elseif idx > (#self._itemT - 1) then
		idx = idx - #self._itemT
	end
	if isforward == true then
		idx = math.floor(idx + self.IndexScrollFactor)		
	elseif isforward == false then
		idx = math.floor(idx + 2*self.IndexScrollFactor)
	end
	self:_setIndex(idx)
end

function UIRotateMenu2:updatePos()
	for k,v in pairs(self._itemT) do
		v:setPosition(self:_getPos(k-1))
		v:setScale(self:_getScale(k-1))
		v:setOpacity(self:_getOpacity(k-1))
		-- v:setRotation3D(cc.vec3(0,self:_getRotation(k-1),0))
		v:setLocalZOrder(self:_getZorder(k-1))
	end
end
function UIRotateMenu2:updatePosWithAni()
	for k,v in pairs(self._itemT) do
		v:stopAllActions()
	end
	for k,v in pairs(self._itemT) do
		local newpos = self:_getPos(k-1)
		v:moveTo({time = self.AniDuration, x = newpos.x, y = newpos.y})
		v:scaleTo({time = self.AniDuration, scale = self:_getScale(k-1)})
		v:fadeTo({time = self.AniDuration, opacity = self:_getOpacity(k-1)})
		-- v:rotateTo({time = self.AniDuration, rotation = cc.vec3(0,self:_getRotation(k-1),0)})
		v:setLocalZOrder(self:_getZorder(k-1))
	end
	local delayTimeAct = cc.DelayTime:create(self.AniDuration)
	local sequence = cc.Sequence:create(delayTimeAct,cc.CallFunc:create(handler(self,self.callFunc)))
	self:runAction(sequence)
end

function UIRotateMenu2:_getPos(idx)
	local cs = self:getContentSize()
	local idxAngle = self:transIdxToCurveFuncVar(idx)
	local posx = self:curveFuncSin(idxAngle,cs.width/2,self.majorSemiAxis)
	local posy = self:curveFuncCos(idxAngle,cs.height/2,-self.minorSemiAxis)
	return cc.p(posx,posy)
end
function UIRotateMenu2:_getZorder(idx)
	local cs = self:getContentSize()
	local idxAngle = self:transIdxToCurveFuncVar(idx)
	return self:curveFuncCos(idxAngle,cs.height/2,self.minorSemiAxis)
end
-- scale: 0.5~1
function UIRotateMenu2:_getScale(idx)
	local idxAngle = self:transIdxToCurveFuncVar(idx)
	return self:curveFuncCos(idxAngle,0.75,0.25)
end
-- opcacity: 129~255
function UIRotateMenu2:_getOpacity(idx)
	local idxAngle = self:transIdxToCurveFuncVar(idx)
	return self:curveFuncCos(idxAngle,192,63)
end
function UIRotateMenu2:_getRotation(idx)
end
function UIRotateMenu2:_getIndex()
	return self._index
end
function UIRotateMenu2:_setIndex( curTmpIdx )
	self._index = curTmpIdx
end

function UIRotateMenu2:callFunc()
	self:rectify()
	local curSp = self._itemT[self:_getIndex() + 1]
	if curSp then
		self.label:setString(curSp:getName())
	end
end

function UIRotateMenu2:onPanelTouchBegan( touch,event )  
    -- cclog("UIRotateMenu2:onPanelTouchBegan")
    if not self.isTouchInside(self,touch) then
    	return false
    end
    for k,v in pairs(self._itemT) do
    	v:stopAllActions()
    end
    return true
end 
function UIRotateMenu2:onPanelTouchMoved( touch,event )
	local deltaX = touch:getDelta().x
	local curTmpIdx = self:transMoveLenToVar(deltaX)
	self:_setIndex(curTmpIdx)
	self:updatePos()
end
function UIRotateMenu2:onPanelTouchEnded( touch,event )
	local deltaX = touch:getLocation().x - touch:getStartLocation().x
	self:rectify(deltaX)
	self:updatePosWithAni()
end

-- 将移动长度转为核心变量
function UIRotateMenu2:transMoveLenToVar(moveLen)
	local cs = self:getContentSize()
	return self._index - moveLen/(cs.width * self.ItemScrollFactor)
end
-- 将当前Idx转为曲线方程自变量
function UIRotateMenu2:transIdxToCurveFuncVar(idx)
	return (idx - self._index) * self._unitAngle
end
function UIRotateMenu2:curveFuncSin(x,a,b) -- x:curAngle
	return a + b * math.sin(x)
end
function UIRotateMenu2:curveFuncCos(x,a,b)
	return a + b * math.cos(x)
end


function UIRotateMenu2:onEnter()
	cclog("UIRotateMenu2:onEnter")
	-- title
    local label = cc.Label:createWithTTF(self.title(), "fonts/arial.ttf", 32)
    self:addChild(label)
    label:setAnchorPoint(cc.p(0.5, 0.5))
    label:setPosition( cc.p(VisibleRect:center().x, VisibleRect:top().y - 50) )

    -- index label
    label = cc.Label:createWithTTF("", "fonts/arial.ttf", 22)
    label:setColor(cc.c3b(255,0,0))
    self:addChild(label)
    label:setAnchorPoint(cc.p(0.5, 0.5))
    label:setPosition( cc.p(VisibleRect:center().x, VisibleRect:top().y - 80) )
    self.label = label

    local ball = cc.Sprite:create("Images/moke1.jpg")
    ball:setName("moke1")
    self:addMenuItem(ball)
    ball = cc.Sprite:create("Images/moke2.jpg")
    ball:setName("moke2")
    self:addMenuItem(ball)
    ball = cc.Sprite:create("Images/moke3.jpg")
    ball:setName("moke3")
    self:addMenuItem(ball)
    ball = cc.Sprite:create("Images/moke4.jpg")
    ball:setName("moke4")
    self:addMenuItem(ball)
    ball = cc.Sprite:create("Images/moke5.jpg")
    ball:setName("moke5")
    self:addMenuItem(ball)
    -- ball = cc.Sprite:create("Images/moke6.jpg")
    -- self:addMenuItem(ball)
end
function UIRotateMenu2:onExit()
end


function UIRotateMenu2Main()
	cclog("UIRotateMenu2Main")
	local scene = cc.Scene:create()
	local rotateMenu = UIRotateMenu2:create()
	scene:addChild(rotateMenu)
	scene:addChild(CreateBackMenuItem())
	return scene
end
可以加入Cocos自带的各种Action实现更多的效果,如下:

修改代码
function UIRotateMenu2:updatePosWithAni()
	for k,v in pairs(self._itemT) do
		v:stopAllActions()
	end
	for k,v in pairs(self._itemT) do
		local newpos = self:_getPos(k-1)
		-- v:moveTo({time = self.AniDuration, x = newpos.x, y = newpos.y})
		local action = cc.MoveTo:create(self.AniDuration, cc.p(newpos.x, newpos.y))
		local move_ease_out = cc.EaseBounceOut:create(action)
		v:runAction(move_ease_out)

		v:scaleTo({time = self.AniDuration, scale = self:_getScale(k-1)})
		v:fadeTo({time = self.AniDuration, opacity = self:_getOpacity(k-1)})
		-- v:rotateTo({time = self.AniDuration, rotation = cc.vec3(0,self:_getRotation(k-1),0)})
		v:setLocalZOrder(self:_getZorder(k-1))
	end
	local delayTimeAct = cc.DelayTime:create(self.AniDuration)
	local sequence = cc.Sequence:create(delayTimeAct,cc.CallFunc:create(handler(self,self.callFunc)))
	self:runAction(sequence)
end

上述两种类型控件原理如下:
1)在滑动时计算处于中间位置的Index;
2)确定采用何种曲线方程计算item的位置、尺度、旋转角度等属性;
3)基于item的index与中心位置的index,计算曲线函数自变量参数,从而进一步计算节点各属性。
至于其他过程,如中心index的矫正,一般无需修改。







猜你喜欢

转载自blog.csdn.net/XIANG__jiangsu/article/details/77983467