技术开发 频道

使用Moblin进行应用程序开发——Clutter之动画

  【IT168 技术】正文开始之前先说几句别的。上月20号发布的Moblin V2 Beta版中,Clutter的版本已经到了0.9,API与clutter 0.8相比有了些变化,两者在API上已不兼容。鉴于pyclutter目前版本依然还是0.8,并且本系列博文更侧重于概念,因此所用的API一律基于clutter 0.8。等clutter 1.0正式版本发布后,我们再来看API的变化。

  之前讲了一点有关Actor与Stage的内容,有了演员和舞台,接下来自然而然就要上台表演,也就是这里所谓的动画了。在Clutter的动画中,有三个概念不得不提一下,他们是——Timeline,Alpha和Behaviour。

  Timeline,即时间轴,也就是表演的时间段,这个概念我想大家应该都能明白,不多废话了。Behaviour,即行为,也就是表演的内容,比如放大、缩小、旋转、透明化等等均属于行为的范畴。最后讲讲Alpha,这个词挺难翻译的,如果放到表演上来理解,Alpha所指的应该是表演方式。比如同样是将某个Actor放大2倍,通过Alpha可以控制是采用线性放大,加速放大还是减速放大等效果。Alpha其实是一个与总帧数和当前帧相关的函数,它的返回值是一个介于0和ALPHA_MAX之间的数,每当一个新的帧产生时都会调用Alpha函数,通过Alpha函数的返回值来确定当前帧的变化。用户可以自定义Alpha函数,也可以使用clutter提供的一些常用Alpha函数。

  一般来说,基于clutter进行动画编程,代码结构通常是这样的:

  1. 创建时间轴ClutterTimeline

  2. 创建ClutterAlpha

  3. 创建ClutterBehaviour

  4. 将Behaviour应用到Actor上

  5. 启动时间轴

  目前Clutter提供的Behaviour包括有Bspline、Ellipse、Depth、Opacity、Path、Rotate和Scale。如果用户觉得这些Behaviour无法满足应用的需求,可以通过实现自定义Behaviour或者使用Timeline的new-frame事件来制作自定义的动画效果。

  回到以前使用的demo 程序——照片浏览器上来,这次要添加的功能是相片的正中显示。之前照片是以随机方位和角度铺在桌面上,现在通过双击照片,图片将以动画的形式移至窗口正中并转至0度。单击正中显示照片将使图片以动画形式回到原先的位置。

  代码如下:

  #!/usr/bin/python

  import sys

  import os

  import random

  import clutter

  STAGE_WIDTH
=1024

  STAGE_HEIGHT
=768

  Dragging
= False

  DraggingPhoto
= None

  Center
= False

  pic_list
= []

  timeline
= None

  alpha
= None

  p_behavior
= None

  r_behavior
= None

  class Photo:

  
'''Photo class'''

  border_width
= 10

  def __init__(self, path, stage):

  self.stage
= stage

  self.path
= path

  self.x
= 0

  self.y
= 0

  self.degree
= 0

  self.drag_start_x
= 0

  self.drag_start_y
= 0

  self.pic
= clutter.Texture()

  self.pic.set_from_file(path)

  self.width
= self.pic.get_width()+2*Photo.border_width

  self.height
= self.pic.get_height()+2*Photo.border_width

  self.frame
= clutter.Rectangle()

  self.frame.set_color(clutter.Color(0xff, 0xff, 0xff, 0xff))

  self.frame.set_position(self.x, self.y)

  self.frame.set_size(self.width, self.height)

  self.group
= clutter.Group()

  self.group.add(self.frame)

  self.group.add(self.pic)

  self.pic.set_position(Photo.border_width, Photo.border_width)

  self.stage.add(self.group)

  self.group.set_reactive(
True)

  self.group.connect(
"button-press-event", self.on_button_press)

  self.group.connect(
"button-release-event", self.on_button_release)

  self.group.connect(
"motion-event", self.on_motion)

  def set_random_position(self):

  stage_width
= self.stage.get_width()

  stage_height
= self.stage.get_height()

  
left = random.randint(0, stage_width)

  top
= random.randint(0, stage_height)

  degree
= random.randint(0, 360)

  self.set_position(
left, top, degree)

  def set_position(self, x, y, degree):

  self.x
= x

  self.y
= y

  self.degree
= degree

  self.group.set_position(x, y)

  self.group.set_rotation(clutter.Z_AXIS, degree, self.width
/2, self.height/2, 0)

  def on_button_press(self, actor, event):

  global Dragging, DraggingPhoto, Center, timeline, alpha, p_behavior, r_behavior

  
if event.button == 1 and event.click_count == 2 and Center == False:

  self.group.raise_top()

  
for pic in pic_list:

  pic.group.set_reactive(
False)

  self.group.set_reactive(
True)

  timeline
= clutter.Timeline(30, 30)

  alpha
= clutter.Alpha(timeline, clutter.ramp_inc_func)

  tx
= int((STAGE_WIDTH-self.width)/2)

  ty
= int((STAGE_HEIGHT-self.height)/2)

  knots
=((self.x, self.y), (tx, ty),)

  p_behavior
= clutter.BehaviourPath(alpha, knots)

  p_behavior.apply(self.group)

  r_behavior
= clutter.BehaviourRotate(clutter.Z_AXIS, self.degree, 0, alpha, True)

  r_behavior.set_center(self.width
/2, self.height/2, 0)

  r_behavior.apply(self.group)

  timeline.start()

  Center
= True

  return
True

  
if event.button == 1 and event.click_count == 1 and Center == True:

  
for pic in pic_list:

  pic.group.set_reactive(
True)

  timeline
= clutter.Timeline(30, 30)

  alpha
= clutter.Alpha(timeline, clutter.ramp_dec_func)

  tx
= int((STAGE_WIDTH-self.width)/2)

  ty
= int((STAGE_HEIGHT-self.height)/2)

  knots
=((self.x, self.y), (tx, ty),)

  p_behavior
= clutter.BehaviourPath(alpha, knots)

  p_behavior.apply(self.group)

  r_behavior
= clutter.BehaviourRotate(clutter.Z_AXIS, self.degree, 0, alpha, True)

  r_behavior.set_center(self.width
/2, self.height/2, 0)

  r_behavior.apply(self.group)

  timeline.start()

  Center
= False

  return
True

  
if event.button == 1 and Dragging == False and Center == False:

  Dragging
= True

  DraggingPhoto
= self

  self.drag_start_x
= event.x

  self.drag_start_y
= event.y

  self.group.raise_top()

  return
True

  return
False

  def on_motion(self, actor, event):

  global Dragging, DraggingPhoto

  
if event.modifier_state & clutter.BUTTON1_MASK and Dragging == True and DraggingPhoto == self:

  dist_x
= event.x - self.drag_start_x

  dist_y
= event.y - self.drag_start_y

  self.group.move_by(dist_x, dist_y)

  self.drag_start_x
= event.x

  self.drag_start_y
= event.y

  return
True

  return
False

  def on_button_release(self, actor, event):

  global Dragging, DraggingPhoto

  
if event.button == 1:

  Dragging
= False

  DraggingPhoto
= None

  return
True

  return
False

  def main(args):

  
if len(args) < 2:

  print
"The number of arguments is less than 2!"

  return
-1

  path
=args[1]

  
if not os.path.exists(path):

  print path,
"doesn't exist!"

  return
-1

  stage
= clutter.Stage()

  stage.set_size(STAGE_WIDTH, STAGE_HEIGHT)

  stage.set_color(clutter.Color(0x00, 0x00, 0x00, 0x00))

  stage.connect(
"destroy", clutter.main_quit)

  
if not path.endswith(os.sep):

  path
+=os.sep

  filelist
= os.listdir(path)

  
for item in filelist:

  pic
=Photo(path+item, stage)

  pic.set_random_position()

  pic_list.append(pic)

  stage.show_all()

  clutter.main()

  
if __name__ == '__main__':

  main(sys.argv)

 

 

0
相关文章