mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Animate basic shapes
This commit is contained in:
parent
56f5fc41fa
commit
c4e33d9dca
8 changed files with 456 additions and 48 deletions
|
@ -5,15 +5,17 @@
|
|||
//! CSS handling for the [`basic-shape`](https://drafts.csswg.org/css-shapes/#typedef-basic-shape)
|
||||
//! types that are generic over their `ToCss` implementations.
|
||||
|
||||
use properties::animated_properties::Animatable;
|
||||
use std::fmt;
|
||||
use style_traits::{HasViewportPercentage, ToCss};
|
||||
use values::animated::ToAnimatedZero;
|
||||
use values::computed::ComputedValueAsSpecified;
|
||||
use values::generics::border::BorderRadius;
|
||||
use values::generics::position::Position;
|
||||
use values::generics::rect::Rect;
|
||||
|
||||
/// A clipping shape, for `clip-path`.
|
||||
pub type ClippingShape<BasicShape, UrlShapeSource> = ShapeSource<BasicShape, GeometryBox, UrlShapeSource>;
|
||||
pub type ClippingShape<BasicShape, Url> = ShapeSource<BasicShape, GeometryBox, Url>;
|
||||
|
||||
/// https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box
|
||||
#[allow(missing_docs)]
|
||||
|
@ -28,7 +30,7 @@ pub enum GeometryBox {
|
|||
impl ComputedValueAsSpecified for GeometryBox {}
|
||||
|
||||
/// A float area shape, for `shape-outside`.
|
||||
pub type FloatAreaShape<BasicShape, UrlShapeSource> = ShapeSource<BasicShape, ShapeBox, UrlShapeSource>;
|
||||
pub type FloatAreaShape<BasicShape, Url> = ShapeSource<BasicShape, ShapeBox, Url>;
|
||||
|
||||
// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box
|
||||
define_css_keyword_enum!(ShapeBox:
|
||||
|
@ -43,8 +45,8 @@ add_impls_for_keyword_enum!(ShapeBox);
|
|||
#[allow(missing_docs)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)]
|
||||
pub enum ShapeSource<BasicShape, ReferenceBox, UrlShapeSource> {
|
||||
Url(UrlShapeSource),
|
||||
pub enum ShapeSource<BasicShape, ReferenceBox, Url> {
|
||||
Url(Url),
|
||||
Shape(BasicShape, Option<ReferenceBox>),
|
||||
Box(ReferenceBox),
|
||||
None,
|
||||
|
@ -120,11 +122,158 @@ define_css_keyword_enum!(FillRule:
|
|||
);
|
||||
add_impls_for_keyword_enum!(FillRule);
|
||||
|
||||
impl<B, T, U> Animatable for ShapeSource<B, T, U>
|
||||
where
|
||||
B: Animatable,
|
||||
T: Clone + PartialEq,
|
||||
{
|
||||
fn add_weighted(
|
||||
&self,
|
||||
other: &Self,
|
||||
self_portion: f64,
|
||||
other_portion: f64,
|
||||
) -> Result<Self, ()> {
|
||||
match (self, other) {
|
||||
(
|
||||
&ShapeSource::Shape(ref this, ref this_box),
|
||||
&ShapeSource::Shape(ref other, ref other_box),
|
||||
) if this_box == other_box => {
|
||||
let shape = this.add_weighted(other, self_portion, other_portion)?;
|
||||
Ok(ShapeSource::Shape(shape, this_box.clone()))
|
||||
},
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
match (self, other) {
|
||||
(
|
||||
&ShapeSource::Shape(ref this, ref this_box),
|
||||
&ShapeSource::Shape(ref other, ref other_box),
|
||||
) if this_box == other_box => {
|
||||
this.compute_distance(other)
|
||||
},
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
match (self, other) {
|
||||
(
|
||||
&ShapeSource::Shape(ref this, ref this_box),
|
||||
&ShapeSource::Shape(ref other, ref other_box),
|
||||
) if this_box == other_box => {
|
||||
this.compute_squared_distance(other)
|
||||
},
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, T, U> ToAnimatedZero for ShapeSource<B, T, U> {
|
||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, T, U> HasViewportPercentage for ShapeSource<B, T, U> {
|
||||
#[inline]
|
||||
fn has_viewport_percentage(&self) -> bool { false }
|
||||
}
|
||||
|
||||
impl<H, V, L> Animatable for BasicShape<H, V, L>
|
||||
where
|
||||
H: Animatable,
|
||||
V: Animatable,
|
||||
L: Animatable + Copy,
|
||||
{
|
||||
fn add_weighted(
|
||||
&self,
|
||||
other: &Self,
|
||||
self_portion: f64,
|
||||
other_portion: f64,
|
||||
) -> Result<Self, ()> {
|
||||
match (self, other) {
|
||||
(&BasicShape::Circle(ref this), &BasicShape::Circle(ref other)) => {
|
||||
Ok(BasicShape::Circle(this.add_weighted(other, self_portion, other_portion)?))
|
||||
},
|
||||
(&BasicShape::Ellipse(ref this), &BasicShape::Ellipse(ref other)) => {
|
||||
Ok(BasicShape::Ellipse(this.add_weighted(other, self_portion, other_portion)?))
|
||||
},
|
||||
(&BasicShape::Inset(ref this), &BasicShape::Inset(ref other)) => {
|
||||
Ok(BasicShape::Inset(this.add_weighted(other, self_portion, other_portion)?))
|
||||
},
|
||||
(&BasicShape::Polygon(ref this), &BasicShape::Polygon(ref other)) => {
|
||||
Ok(BasicShape::Polygon(this.add_weighted(other, self_portion, other_portion)?))
|
||||
},
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
match (self, other) {
|
||||
(&BasicShape::Circle(ref this), &BasicShape::Circle(ref other)) => {
|
||||
this.compute_distance(other)
|
||||
},
|
||||
(&BasicShape::Ellipse(ref this), &BasicShape::Ellipse(ref other)) => {
|
||||
this.compute_distance(other)
|
||||
},
|
||||
(&BasicShape::Inset(ref this), &BasicShape::Inset(ref other)) => {
|
||||
this.compute_distance(other)
|
||||
},
|
||||
(&BasicShape::Polygon(ref this), &BasicShape::Polygon(ref other)) => {
|
||||
this.compute_distance(other)
|
||||
},
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
match (self, other) {
|
||||
(&BasicShape::Circle(ref this), &BasicShape::Circle(ref other)) => {
|
||||
this.compute_squared_distance(other)
|
||||
},
|
||||
(&BasicShape::Ellipse(ref this), &BasicShape::Ellipse(ref other)) => {
|
||||
this.compute_squared_distance(other)
|
||||
},
|
||||
(&BasicShape::Inset(ref this), &BasicShape::Inset(ref other)) => {
|
||||
this.compute_squared_distance(other)
|
||||
},
|
||||
(&BasicShape::Polygon(ref this), &BasicShape::Polygon(ref other)) => {
|
||||
this.compute_squared_distance(other)
|
||||
},
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L> Animatable for InsetRect<L>
|
||||
where
|
||||
L: Animatable + Copy,
|
||||
{
|
||||
fn add_weighted(
|
||||
&self,
|
||||
other: &Self,
|
||||
self_portion: f64,
|
||||
other_portion: f64,
|
||||
) -> Result<Self, ()> {
|
||||
let rect = self.rect.add_weighted(&other.rect, self_portion, other_portion)?;
|
||||
let round = self.round.add_weighted(&other.round, self_portion, other_portion)?;
|
||||
Ok(InsetRect { rect, round })
|
||||
}
|
||||
|
||||
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
Ok(self.compute_squared_distance(other)?.sqrt())
|
||||
}
|
||||
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
Ok(
|
||||
self.rect.compute_squared_distance(&other.rect)? +
|
||||
self.round.compute_squared_distance(&other.round)?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L> ToCss for InsetRect<L>
|
||||
where L: ToCss + PartialEq
|
||||
{
|
||||
|
@ -139,11 +288,151 @@ impl<L> ToCss for InsetRect<L>
|
|||
}
|
||||
}
|
||||
|
||||
impl<H, V, L> Animatable for Circle<H, V, L>
|
||||
where
|
||||
H: Animatable,
|
||||
V: Animatable,
|
||||
L: Animatable,
|
||||
{
|
||||
fn add_weighted(
|
||||
&self,
|
||||
other: &Self,
|
||||
self_portion: f64,
|
||||
other_portion: f64,
|
||||
) -> Result<Self, ()> {
|
||||
let position = self.position.add_weighted(&other.position, self_portion, other_portion)?;
|
||||
let radius = self.radius.add_weighted(&other.radius, self_portion, other_portion)?;
|
||||
Ok(Circle { position, radius })
|
||||
}
|
||||
|
||||
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
Ok(self.compute_squared_distance(other)?.sqrt())
|
||||
}
|
||||
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
Ok(
|
||||
self.position.compute_squared_distance(&other.position)? +
|
||||
self.radius.compute_squared_distance(&other.radius)?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, V, L> Animatable for Ellipse<H, V, L>
|
||||
where
|
||||
H: Animatable,
|
||||
V: Animatable,
|
||||
L: Animatable,
|
||||
{
|
||||
fn add_weighted(
|
||||
&self,
|
||||
other: &Self,
|
||||
self_portion: f64,
|
||||
other_portion: f64,
|
||||
) -> Result<Self, ()> {
|
||||
let position = self.position.add_weighted(&other.position, self_portion, other_portion)?;
|
||||
let semiaxis_x = self.semiaxis_x.add_weighted(&other.semiaxis_x, self_portion, other_portion)?;
|
||||
let semiaxis_y = self.semiaxis_y.add_weighted(&other.semiaxis_y, self_portion, other_portion)?;
|
||||
Ok(Ellipse { position, semiaxis_x, semiaxis_y })
|
||||
}
|
||||
|
||||
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
Ok(self.compute_squared_distance(other)?.sqrt())
|
||||
}
|
||||
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
Ok(
|
||||
self.position.compute_squared_distance(&other.position)? +
|
||||
self.semiaxis_x.compute_squared_distance(&other.semiaxis_x)? +
|
||||
self.semiaxis_y.compute_squared_distance(&other.semiaxis_y)?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L> Animatable for ShapeRadius<L>
|
||||
where
|
||||
L: Animatable,
|
||||
{
|
||||
fn add_weighted(
|
||||
&self,
|
||||
other: &Self,
|
||||
self_portion: f64,
|
||||
other_portion: f64,
|
||||
) -> Result<Self, ()> {
|
||||
match (self, other) {
|
||||
(&ShapeRadius::Length(ref this), &ShapeRadius::Length(ref other)) => {
|
||||
Ok(ShapeRadius::Length(this.add_weighted(other, self_portion, other_portion)?))
|
||||
},
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
match (self, other) {
|
||||
(&ShapeRadius::Length(ref this), &ShapeRadius::Length(ref other)) => {
|
||||
this.compute_distance(other)
|
||||
},
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
match (self, other) {
|
||||
(&ShapeRadius::Length(ref this), &ShapeRadius::Length(ref other)) => {
|
||||
this.compute_squared_distance(other)
|
||||
},
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L> Default for ShapeRadius<L> {
|
||||
#[inline]
|
||||
fn default() -> Self { ShapeRadius::ClosestSide }
|
||||
}
|
||||
|
||||
impl<L> Animatable for Polygon<L>
|
||||
where
|
||||
L: Animatable,
|
||||
{
|
||||
fn add_weighted(
|
||||
&self,
|
||||
other: &Self,
|
||||
self_portion: f64,
|
||||
other_portion: f64,
|
||||
) -> Result<Self, ()> {
|
||||
if self.fill != other.fill {
|
||||
return Err(());
|
||||
}
|
||||
if self.coordinates.len() != other.coordinates.len() {
|
||||
return Err(());
|
||||
}
|
||||
let coordinates = self.coordinates.iter().zip(other.coordinates.iter()).map(|(this, other)| {
|
||||
let x = this.0.add_weighted(&other.0, self_portion, other_portion)?;
|
||||
let y = this.1.add_weighted(&other.1, self_portion, other_portion)?;
|
||||
Ok((x, y))
|
||||
}).collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(Polygon { fill: self.fill, coordinates })
|
||||
}
|
||||
|
||||
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
Ok(self.compute_squared_distance(other)?.sqrt())
|
||||
}
|
||||
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
if self.fill != other.fill {
|
||||
return Err(());
|
||||
}
|
||||
if self.coordinates.len() != other.coordinates.len() {
|
||||
return Err(());
|
||||
}
|
||||
self.coordinates.iter().zip(other.coordinates.iter()).map(|(this, other)| {
|
||||
let x = this.0.compute_squared_distance(&other.0)?;
|
||||
let y = this.1.compute_squared_distance(&other.1)?;
|
||||
Ok(x + y)
|
||||
}).sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: ToCss> ToCss for Polygon<L> {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
dest.write_str("polygon(")?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue