Mercurial > MadButterfly
view pyink/html5css3.py @ 1362:bb76f9d57363
Generate CSS3 transition rules
author | Thinker K.F. Li <thinker@codemud.net> |
---|---|
date | Fri, 18 Feb 2011 09:00:02 +0800 |
parents | cf5ed0b8f45d |
children | a05ea7fa43ec |
line wrap: on
line source
import pybExtension from domview import component_manager, layers_parser, scenes_parser from trait import composite, trait, require def _scene_node_range(node): start = node.getAttribute('start') try: end = node.getAttribute('end') except: end = start pass try: scene_type = node.getAttribute('type') except: scene_type = 'normal' pass return int(start), int(end), scene_type @composite class dom_parser(object): use_traits = (component_manager, layers_parser, scenes_parser) provide_traits = {layers_parser.get_scene: '_scenes_get_scene'} method_map_traits = { scenes_parser._find_maxframe: '_find_maxframe', scenes_parser._collect_all_scenes: '_collect_all_scenes', scenes_parser._collect_node_ids: '_collect_node_ids', component_manager._start_component_manager: '_start_component_manager', scenes_parser.get_scene: '_scenes_get_scene' } def __init__(self): self._metadata_node = None self._scenes_node = None self._doc = None self._root = None self._layers = [] self._layers_parent = None self._maxframe = 0 self.current = 0 self._id2node = {} self._group2scene = {} pass def _find_meta(self): for child in self._root.childList(): if child.name() == 'svg:metadata': self._metadata_node = child break pass else: raise RuntimeError, 'can not find \'svg:metadata\' node' for child in self._metadata_node.childList(): if child.name() == 'ns0:scenes': self._scenes_node = child break pass else: raise RuntimeError, \ 'can not find \'ns0:scenes\' node under \'svg:metadata\'' pass def start_handle(self, doc): self._doc = doc self._root = doc.root() self._layers_parent = self._root self._find_meta() self._collect_node_ids() self._collect_all_scenes() self.parse_all_layers() self._start_component_manager() pass def reset(self): self.reset_layers() pass def get_maxframe(self): return self._maxframe def _find_scene_node(self, frame_idx, layer_idx): layer = self._layers[layer_idx] for scene_node in layer.scenes: start, end, scene_type = _scene_node_range(scene_node) if start <= frame_idx and frame_idx <= end: return scene_node pass pass def get_scene(self, frame_idx, layer_idx): scene_node = self._find_scene_node(frame_idx, layer_idx) if scene_node: start, end, scene_type = _scene_node_range(scene_node) return start, end, scene_type return None def get_scene_group(self, frame_idx, layer_idx): scene_node = self._find_scene_node(frame_idx, layer_idx) if scene_node: gid = scene_node.getAttribute('ref') scene_group = self.get_node(gid) return scene_group return None pass def _print_level(txt, lvl, out): indent = ' ' * lvl print >> out, '%s%s' % (indent, txt) pass def _print_node_open(node, lvl, out): attrs = [] for attrname in node.allAttributes(): attrvalue = node.getAttribute(attrname) attr = '%s="%s"' % (attrname, attrvalue) attrs.append(attr) pass if attrs: attrs_str = ' '.join(attrs) line = '<%s %s>' % (node.name(), attrs_str) else: line = '<%s>' % (node.name()) pass _print_level(line, lvl, out) pass def _print_node_close(node, lvl, out): line = '</%s>' % (node.name()) _print_level(line, lvl, out) pass def _print_node_single(node, lvl, out): attrs = [] for attrname in node.allAttributes(): attrvalue = node.getAttribute(attrname) attr = '%s="%s"' % (attrname, attrvalue) attrs.append(attr) pass if attrs: attrs_str = ' '.join(attrs) line = '<%s %s/>' % (node.name(), attrs_str) else: line = '<%s/>' % (node.name()) pass _print_level(line, lvl, out) pass def _print_node_content(node, lvl, out): line = node.content() _print_level(line, lvl, out) pass def _print_subtree(node, lvl, out): children = node.childList() if not children: if node.name() != 'string': _print_node_single(node, lvl, out) else: _print_node_content(node, lvl, out) pass return _print_node_open(node, lvl, out) for child in children: _print_subtree(child, lvl + 1, out) pass _print_node_close(node, lvl, out) pass ## \brief CSS3 animation code generator. # # This trait parses and generate transition code, in CSS3, for two # nodes. # @trait class css3_ani_gen(object): _parse_ani_attrs = require _passing = lambda name, value: (name, str(value)) _trans_transform = lambda name, value: \ (name, 'matrix(' + ','.join([str(e) for e in value]) + ')') _translators = {'x': _passing, 'y': _passing, 'width': _passing, 'height': _passing, 'opacity': _passing, 'transform': _trans_transform } del _trans_transform del _passing @staticmethod def _ani_attr_to_css3(attrname, attrvalue, css): translator = css3_ani_gen._translators[attrname] cssname, cssvalue = translator(attrname, attrvalue) css[cssname] = cssvalue pass def _make_css3_transition(self, node1, node2, duration): from tween import _normalize_attrs ani_attrs1 = self._parse_ani_attrs(node1) ani_attrs2 = self._parse_ani_attrs(node2) _normalize_attrs(node1, ani_attrs1, node2, ani_attrs2) css = {} attrnames = set(ani_attrs1.keys() + ani_attrs2.keys()) for attrname in attrnames: attrvalue = ani_attrs2[attrname] self._ani_attr_to_css3(attrname, attrvalue, css) pass properties = css.keys() css['transition-property'] = ', '.join(properties) css['transition-duration'] = '%gs' % (duration) return css def _write_css(self, selector, css_props, out): print >> out, '%s {' % (selector) for prop_name, prop_value in css_props.items(): print >> out, ' %s: %s' % (prop_name, prop_value) pass print >> out, '}' pass pass @composite class html5css3_ext(pybExtension.PYBindExtImp): use_traits = (css3_ani_gen,) method_map_traits = {css3_ani_gen._make_css3_transition: '_make_css3_transition', css3_ani_gen._write_css: '_write_css' } _frame_rate = 12 def __init__(self): self._stylesheet = {} pass @staticmethod def _parse_ani_attrs(node): from tween import _parse_attr_ani, _parse_style_ani attrs = {} _parse_attr_ani(node, attrs) _parse_style_ani(node, attrs) return attrs def save(self, module, doc, filename): import sys parser = dom_parser() self._parser = parser parser.start_handle(doc.rdoc) print parser._maxframe print doc.rdoc.root().allAttributes() print parser.all_comp_names() print 'save to ' + filename self._handle_transition_layers() print self._stylesheet.keys() pass ## \brief Find all animation pairs. # # An animation pair is two nodes, one from start scene group and # another from stop scene group. The node from start scene group # will transite to the state of the node from stop scene group. # This method is responsible for finding all pairs. # @staticmethod def _find_transition_pairs(start_scene_group, stop_scene_group): # Collect all nodes in stop scene stop_nodes = {} node = stop_scene_group.firstChild() while node: try: node_label = node.getAttribute("ns0:duplicate-src") stop_nodes[node_label] = node except: pass node = node.next() pass # Collect all nodes in start scene start_nodes = {} node = start_scene_group.firstChild() while node: try: node_label = node.getAttribute("id") start_nodes[node_label] = node except: pass node = node.next() pass pairs = [] for start_node in start_scene_group.childList(): start_node_id = start_node.getAttribute('id') try: stop_node = stop_nodes[start_node_id] except: stop_node = start_node pass pairs.append((start_node, stop_node)) pass return pairs def _handle_transition_layer(self, layer_idx): parser = self._parser maxframe = parser.get_maxframe() stylesheet = self._stylesheet frame_idx = 0 while frame_idx < maxframe: scene = parser.get_scene(frame_idx, layer_idx) if not scene: frame_idx = frame_idx + 1 continue start, end, tween_type = scene if start == end: frame_idx = frame_idx + 1 continue next_start = end + 1 scene = parser.get_scene(next_start, layer_idx) if not scene: frame_idx = next_start + 1 continue scene_group = parser.get_scene_group(start, layer_idx) next_scene_group = parser.get_scene_group(next_start, layer_idx) duration = float(next_start - start) / self._frame_rate # # Generate style for every transition pair # transition_pairs = \ self._find_transition_pairs(scene_group, next_scene_group) for start_node, stop_node in transition_pairs: css_props = self._make_css3_transition(start_node, stop_node, duration) node_id = start_node.getAttribute('id') selector = '.transition%d #%s' % (start, node_id) stylesheet[selector] = css_props pass frame_idx = next_start pass pass def _handle_transition_layers(self): num_layers = self._parser.get_layer_num() for layer_idx in range(num_layers): self._handle_transition_layer(layer_idx) pass pass pass extension = (html5css3_ext(), 'net.scribboo.html5css3', 'HTML5/CSS3 exporter', 'output', {'extension': '.html', 'mimetype': 'text/html', '_filetypename': 'HTML5/CSS3 (*.html)'})