animation.rs (8565B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 //! This example creates a 200x200 white rect and allows the user to move it 6 //! around by using the arrow keys and rotate with '<'/'>'. 7 //! It does this by using the animation API. 8 9 //! The example also features seamless opaque/transparent split of a 10 //! rounded cornered rectangle, which is done automatically during the 11 //! scene building for render optimization. 12 13 extern crate euclid; 14 extern crate gleam; 15 extern crate glutin; 16 extern crate webrender; 17 extern crate winit; 18 19 #[path = "common/boilerplate.rs"] 20 mod boilerplate; 21 22 use crate::boilerplate::{Example, HandyDandyRectBuilder}; 23 use euclid::Angle; 24 use webrender::api::*; 25 use webrender::render_api::*; 26 use webrender::api::units::*; 27 28 29 struct App { 30 property_key0: PropertyBindingKey<LayoutTransform>, 31 property_key1: PropertyBindingKey<LayoutTransform>, 32 property_key2: PropertyBindingKey<LayoutTransform>, 33 opacity_key: PropertyBindingKey<f32>, 34 opacity: f32, 35 angle0: f32, 36 angle1: f32, 37 angle2: f32, 38 } 39 40 impl App { 41 fn add_rounded_rect( 42 &mut self, 43 bounds: LayoutRect, 44 color: ColorF, 45 builder: &mut DisplayListBuilder, 46 pipeline_id: PipelineId, 47 property_key: PropertyBindingKey<LayoutTransform>, 48 opacity_key: Option<PropertyBindingKey<f32>>, 49 spatial_tree_item_key: SpatialTreeItemKey, 50 ) { 51 let filters = match opacity_key { 52 Some(opacity_key) => { 53 vec![ 54 FilterOp::Opacity(PropertyBinding::Binding(opacity_key, self.opacity), self.opacity), 55 ] 56 } 57 None => { 58 vec![] 59 } 60 }; 61 62 let spatial_id = builder.push_reference_frame( 63 bounds.min, 64 SpatialId::root_scroll_node(pipeline_id), 65 TransformStyle::Flat, 66 PropertyBinding::Binding(property_key, LayoutTransform::identity()), 67 ReferenceFrameKind::Transform { 68 is_2d_scale_translation: false, 69 should_snap: false, 70 paired_with_perspective: false, 71 }, 72 spatial_tree_item_key, 73 ); 74 75 builder.push_simple_stacking_context_with_filters( 76 LayoutPoint::zero(), 77 spatial_id, 78 PrimitiveFlags::IS_BACKFACE_VISIBLE, 79 &filters, 80 &[], 81 ); 82 83 let space_and_clip = SpaceAndClipInfo { 84 spatial_id, 85 clip_chain_id: ClipChainId::INVALID, 86 }; 87 let clip_bounds = LayoutRect::from_size(bounds.size()); 88 let complex_clip = ComplexClipRegion { 89 rect: clip_bounds, 90 radii: BorderRadius::uniform(30.0), 91 mode: ClipMode::Clip, 92 }; 93 let clip_id = builder.define_clip_rounded_rect( 94 space_and_clip.spatial_id, 95 complex_clip, 96 ); 97 let clip_chain_id = builder.define_clip_chain(None, [clip_id]); 98 99 // Fill it with a white rect 100 builder.push_rect( 101 &CommonItemProperties::new( 102 LayoutRect::from_size(bounds.size()), 103 SpaceAndClipInfo { 104 spatial_id, 105 clip_chain_id, 106 } 107 ), 108 LayoutRect::from_size(bounds.size()), 109 color, 110 ); 111 112 builder.pop_stacking_context(); 113 builder.pop_reference_frame(); 114 } 115 } 116 117 impl Example for App { 118 const WIDTH: u32 = 2048; 119 const HEIGHT: u32 = 1536; 120 121 fn render( 122 &mut self, 123 _api: &mut RenderApi, 124 builder: &mut DisplayListBuilder, 125 _txn: &mut Transaction, 126 _device_size: DeviceIntSize, 127 pipeline_id: PipelineId, 128 _document_id: DocumentId, 129 ) { 130 let opacity_key = self.opacity_key; 131 132 let bounds = (150, 150).to(250, 250); 133 let key0 = self.property_key0; 134 self.add_rounded_rect( 135 bounds, 136 ColorF::new(1.0, 0.0, 0.0, 0.5), 137 builder, 138 pipeline_id, 139 key0, 140 Some(opacity_key), 141 SpatialTreeItemKey::new(0, 0) 142 ); 143 144 let bounds = (400, 400).to(600, 600); 145 let key1 = self.property_key1; 146 self.add_rounded_rect( 147 bounds, 148 ColorF::new(0.0, 1.0, 0.0, 0.5), 149 builder, 150 pipeline_id, 151 key1, 152 None, 153 SpatialTreeItemKey::new(0, 1) 154 ); 155 156 let bounds = (200, 500).to(350, 580); 157 let key2 = self.property_key2; 158 self.add_rounded_rect( 159 bounds, 160 ColorF::new(0.0, 0.0, 1.0, 0.5), 161 builder, 162 pipeline_id, 163 key2, 164 None, 165 SpatialTreeItemKey::new(0, 2) 166 ); 167 } 168 169 fn on_event( 170 &mut self, 171 win_event: winit::event::WindowEvent, 172 _window: &winit::window::Window, 173 api: &mut RenderApi, 174 document_id: DocumentId 175 ) -> bool { 176 let mut rebuild_display_list = false; 177 178 match win_event { 179 winit::event::WindowEvent::KeyboardInput { 180 input: winit::event::KeyboardInput { 181 state: winit::event::ElementState::Pressed, 182 virtual_keycode: Some(key), 183 .. 184 }, 185 .. 186 } => { 187 let (delta_angle, delta_opacity) = match key { 188 winit::event::VirtualKeyCode::Down => (0.0, -0.1), 189 winit::event::VirtualKeyCode::Up => (0.0, 0.1), 190 winit::event::VirtualKeyCode::Right => (1.0, 0.0), 191 winit::event::VirtualKeyCode::Left => (-1.0, 0.0), 192 winit::event::VirtualKeyCode::R => { 193 rebuild_display_list = true; 194 (0.0, 0.0) 195 } 196 _ => return false, 197 }; 198 // Update the transform based on the keyboard input and push it to 199 // webrender using the generate_frame API. This will recomposite with 200 // the updated transform. 201 self.opacity += delta_opacity; 202 self.angle0 += delta_angle * 0.1; 203 self.angle1 += delta_angle * 0.2; 204 self.angle2 -= delta_angle * 0.15; 205 let xf0 = LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::radians(self.angle0)); 206 let xf1 = LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::radians(self.angle1)); 207 let xf2 = LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::radians(self.angle2)); 208 let mut txn = Transaction::new(); 209 txn.reset_dynamic_properties(); 210 txn.append_dynamic_properties( 211 DynamicProperties { 212 transforms: vec![ 213 PropertyValue { 214 key: self.property_key0, 215 value: xf0, 216 }, 217 PropertyValue { 218 key: self.property_key1, 219 value: xf1, 220 }, 221 PropertyValue { 222 key: self.property_key2, 223 value: xf2, 224 }, 225 ], 226 floats: vec![ 227 PropertyValue { 228 key: self.opacity_key, 229 value: self.opacity, 230 } 231 ], 232 colors: vec![], 233 }, 234 ); 235 txn.generate_frame(0, true, false, RenderReasons::empty()); 236 api.send_transaction(document_id, txn); 237 } 238 _ => (), 239 } 240 241 rebuild_display_list 242 } 243 } 244 245 fn main() { 246 let mut app = App { 247 property_key0: PropertyBindingKey::new(42), // arbitrary magic number 248 property_key1: PropertyBindingKey::new(44), // arbitrary magic number 249 property_key2: PropertyBindingKey::new(45), // arbitrary magic number 250 opacity_key: PropertyBindingKey::new(43), 251 opacity: 0.5, 252 angle0: 0.0, 253 angle1: 0.0, 254 angle2: 0.0, 255 }; 256 boilerplate::main_wrapper(&mut app, None); 257 }