CSS animations
This commit is contained in:
@@ -908,6 +908,10 @@ static func parse_utility_class_internal(rule: CSSRule, utility_name: String) ->
|
||||
for property in transform_properties:
|
||||
rule.properties[property] = transform_properties[property]
|
||||
return
|
||||
|
||||
if utility_name in ["transition", "transition-colors", "transition-opacity", "transition-transform"]:
|
||||
rule.properties[utility_name] = "200ms"
|
||||
return
|
||||
|
||||
# Handle more utility classes as needed
|
||||
# Add more cases here for other utilities
|
||||
|
||||
@@ -2636,12 +2636,13 @@ var HTML_CONTENT_TRANSFORM_TEST = """<head>
|
||||
<div style="demo-item">
|
||||
<h2>Hover Effect</h2>
|
||||
<div style="description">Hover to see scale effect</div>
|
||||
<div style="demo-box hover:scale-110">Box</div>
|
||||
<div style="demo-box hover:scale-110 transition hover:rotate-45">Box</div>
|
||||
<div style="code-block">hover:scale-110</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<button style="hover:scale-110 transition">Button</button>
|
||||
|
||||
<div style="bg-white p-6 rounded-xl shadow-lg mt-8 max-w-4xl mx-auto">
|
||||
<h2>📝 Transform Utility Reference</h2>
|
||||
<div style="text-[#475569]">
|
||||
|
||||
@@ -680,67 +680,58 @@ static func apply_transform_properties(node: Control, styles: Dictionary) -> voi
|
||||
apply_transform_properties_direct(node, styles)
|
||||
|
||||
static func apply_transform_properties_direct(node: Control, styles: Dictionary) -> void:
|
||||
var has_transform = false
|
||||
var scale_x = 1.0
|
||||
var scale_y = 1.0
|
||||
var rotation_z = 0.0
|
||||
var scale_x = styles.get("scale-x", 1.0)
|
||||
var scale_y = styles.get("scale-y", 1.0)
|
||||
var rotation = styles.get("rotate", 0.0)
|
||||
|
||||
# Check for scale properties
|
||||
if styles.has("scale-x"):
|
||||
scale_x = styles["scale-x"]
|
||||
has_transform = true
|
||||
if styles.has("scale-y"):
|
||||
scale_y = styles["scale-y"]
|
||||
has_transform = true
|
||||
|
||||
# Check for rotation properties (prioritize Z-axis for 2D)
|
||||
if styles.has("rotate-z"):
|
||||
rotation_z = styles["rotate-z"]
|
||||
has_transform = true
|
||||
elif styles.has("rotate-x"):
|
||||
rotation_z = styles["rotate-x"]
|
||||
has_transform = true
|
||||
elif styles.has("rotate-y"):
|
||||
rotation_z = styles["rotate-y"]
|
||||
has_transform = true
|
||||
var has_transform = scale_x != 1.0 or scale_y != 1.0 or rotation != 0.0
|
||||
var duration = get_transition_duration(styles)
|
||||
|
||||
if has_transform:
|
||||
# Set metadata flag to tell FlexContainer to preserve transforms
|
||||
node.set_meta("css_transform_applied", true)
|
||||
node.set_meta("pending_scale", Vector2(scale_x, scale_y))
|
||||
node.set_meta("pending_rotation", rotation_z)
|
||||
|
||||
# Apply immediately
|
||||
node.scale = Vector2(scale_x, scale_y)
|
||||
node.rotation = rotation_z
|
||||
# Set pivot point to center
|
||||
node.pivot_offset = node.size / 2
|
||||
|
||||
# Reapply after FlexContainer layout using timer
|
||||
var timer = Timer.new()
|
||||
timer.wait_time = 0.1
|
||||
timer.one_shot = true
|
||||
timer.autostart = true
|
||||
var main_scene = Engine.get_main_loop().current_scene
|
||||
if main_scene:
|
||||
timer.timeout.connect(func(): _reapply_transforms(node))
|
||||
main_scene.add_child(timer)
|
||||
if duration > 0:
|
||||
animate_transform(node, Vector2(scale_x, scale_y), rotation, duration)
|
||||
else:
|
||||
node.scale = Vector2(scale_x, scale_y)
|
||||
node.rotation = rotation
|
||||
await_and_restore_transform(node, Vector2(scale_x, scale_y), rotation)
|
||||
else:
|
||||
# Reset transforms to default when no transforms are specified
|
||||
node.scale = Vector2.ONE
|
||||
node.rotation = 0.0
|
||||
if duration > 0:
|
||||
animate_transform(node, Vector2.ONE, 0.0, duration)
|
||||
else:
|
||||
node.scale = Vector2.ONE
|
||||
node.rotation = 0.0
|
||||
if node.has_meta("css_transform_applied"):
|
||||
node.remove_meta("css_transform_applied")
|
||||
|
||||
static func _reapply_transforms(node: Control) -> void:
|
||||
if not is_instance_valid(node):
|
||||
return
|
||||
static func get_transition_duration(styles: Dictionary) -> float:
|
||||
if styles.has("transition-transform"):
|
||||
return parse_transition_duration(styles["transition-transform"])
|
||||
elif styles.has("transition"):
|
||||
return parse_transition_duration(styles["transition"])
|
||||
return 0.0
|
||||
|
||||
static func parse_transition_duration(value: String) -> float:
|
||||
if value.ends_with("ms"):
|
||||
return float(value.replace("ms", "")) / 1000.0
|
||||
elif value.ends_with("s"):
|
||||
return float(value.replace("s", ""))
|
||||
return float(value) if value.is_valid_float() else 0.0
|
||||
|
||||
static func animate_transform(node: Control, target_scale: Vector2, target_rotation: float, duration: float) -> void:
|
||||
var tween = node.create_tween()
|
||||
tween.set_parallel(true)
|
||||
tween.tween_property(node, "scale", target_scale, duration).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN_OUT)
|
||||
tween.tween_property(node, "rotation", target_rotation, duration).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN_OUT)
|
||||
|
||||
static func await_and_restore_transform(node: Control, target_scale: Vector2, target_rotation: float) -> void:
|
||||
var tree = Engine.get_main_loop()
|
||||
|
||||
if node.has_meta("pending_scale"):
|
||||
node.scale = node.get_meta("pending_scale")
|
||||
|
||||
if node.has_meta("pending_rotation"):
|
||||
node.rotation = node.get_meta("pending_rotation")
|
||||
|
||||
if node.has_meta("pending_scale"):
|
||||
node.remove_meta("pending_scale")
|
||||
if node.has_meta("pending_rotation"):
|
||||
node.remove_meta("pending_rotation")
|
||||
await tree.process_frame
|
||||
node.scale = target_scale
|
||||
node.rotation = target_rotation
|
||||
node.pivot_offset = node.size / 2
|
||||
|
||||
@@ -51,6 +51,9 @@ func apply_button_styles(element: HTMLParser.HTMLElement, parser: HTMLParser) ->
|
||||
# Apply text color with state-dependent colors
|
||||
apply_button_text_color(button_node, styles, hover_styles, active_styles)
|
||||
|
||||
# Apply transform properties with hover support
|
||||
apply_button_transforms(button_node, styles, hover_styles)
|
||||
|
||||
# Apply background color (hover: + active:)
|
||||
if styles.has("background-color"):
|
||||
var normal_color: Color = styles["background-color"]
|
||||
@@ -194,3 +197,52 @@ func apply_size_and_flags(ctrl: Control, width: Variant, height: Variant) -> voi
|
||||
ctrl.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
if height != null:
|
||||
ctrl.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
|
||||
|
||||
func apply_button_transforms(button: Button, normal_styles: Dictionary, hover_styles: Dictionary) -> void:
|
||||
# Apply normal transforms to the parent HBoxContainer (self)
|
||||
StyleManager.apply_transform_properties_direct(self, normal_styles)
|
||||
|
||||
# Set pivot point to center of the container
|
||||
self.pivot_offset = self.size / 2
|
||||
|
||||
# Set up hover transforms if present
|
||||
var has_hover_transforms = hover_styles.has("scale-x") or hover_styles.has("scale-y") or hover_styles.has("rotate")
|
||||
if has_hover_transforms:
|
||||
# Store original and hover values
|
||||
var original_scale = Vector2(
|
||||
normal_styles.get("scale-x", 1.0),
|
||||
normal_styles.get("scale-y", 1.0)
|
||||
)
|
||||
var original_rotation = normal_styles.get("rotate", 0.0)
|
||||
|
||||
var hover_scale = Vector2(
|
||||
hover_styles.get("scale-x", original_scale.x),
|
||||
hover_styles.get("scale-y", original_scale.y)
|
||||
)
|
||||
var hover_rotation = hover_styles.get("rotate", original_rotation)
|
||||
|
||||
# Get transition duration
|
||||
var duration = StyleManager.get_transition_duration(normal_styles)
|
||||
if duration == 0:
|
||||
duration = StyleManager.get_transition_duration(hover_styles)
|
||||
|
||||
# Connect hover events to the button but apply transforms to self
|
||||
button.mouse_entered.connect(func():
|
||||
# Update pivot point in case size changed
|
||||
self.pivot_offset = self.size / 2
|
||||
if duration > 0:
|
||||
StyleManager.animate_transform(self, hover_scale, hover_rotation, duration)
|
||||
else:
|
||||
self.scale = hover_scale
|
||||
self.rotation = hover_rotation
|
||||
)
|
||||
|
||||
button.mouse_exited.connect(func():
|
||||
# Update pivot point in case size changed
|
||||
self.pivot_offset = self.size / 2
|
||||
if duration > 0:
|
||||
StyleManager.animate_transform(self, original_scale, original_rotation, duration)
|
||||
else:
|
||||
self.scale = original_scale
|
||||
self.rotation = original_rotation
|
||||
)
|
||||
|
||||
@@ -132,26 +132,12 @@ static func parse_scale_utility(utility_name: String) -> Dictionary:
|
||||
static func parse_rotation_utility(utility_name: String) -> Dictionary:
|
||||
var result = {}
|
||||
|
||||
if utility_name.begins_with("rotate-x-"):
|
||||
var val = utility_name.substr(9) # after "rotate-x-"
|
||||
if val.begins_with("[") and val.ends_with("]"):
|
||||
val = val.substr(1, val.length() - 2)
|
||||
var rotation = parse_rotation(val)
|
||||
result["rotate-x"] = rotation
|
||||
return result
|
||||
elif utility_name.begins_with("rotate-y-"):
|
||||
var val = utility_name.substr(9) # after "rotate-y-"
|
||||
if val.begins_with("[") and val.ends_with("]"):
|
||||
val = val.substr(1, val.length() - 2)
|
||||
var rotation = parse_rotation(val)
|
||||
result["rotate-y"] = rotation
|
||||
return result
|
||||
elif utility_name.begins_with("rotate-"):
|
||||
if utility_name.begins_with("rotate-"):
|
||||
var val = utility_name.substr(7) # after "rotate-"
|
||||
if val.begins_with("[") and val.ends_with("]"):
|
||||
val = val.substr(1, val.length() - 2)
|
||||
var rotation = parse_rotation(val)
|
||||
result["rotate-z"] = rotation # Default rotation is around Z-axis
|
||||
result["rotate"] = rotation
|
||||
return result
|
||||
|
||||
return result
|
||||
|
||||
@@ -43,8 +43,9 @@ static func init_patterns():
|
||||
"^cursor-[a-zA-Z-]+$", # cursor types
|
||||
"^scale-(x-|y-)?\\d+$", # scale utilities like scale-100, scale-x-75, scale-y-150
|
||||
"^scale-(x-|y-)?\\[.*\\]$", # custom scale values like scale-[1.5], scale-x-[2.0]
|
||||
"^rotate-(x-|y-)?\\d+$", # rotation utilities like rotate-45, rotate-x-90, rotate-y-180
|
||||
"^rotate-(x-|y-)?\\[.*\\]$", # custom rotation values like rotate-[45deg], rotate-x-[90deg], rotate-y-[3.5rad]
|
||||
"^rotate-\\d+$", # rotation utilities like rotate-45, rotate-90
|
||||
"^rotate-\\[.*\\]$", # custom rotation values like rotate-[45deg], rotate-[3.5rad]
|
||||
"^transition(-colors|-opacity|-transform)?$", # transition utilities
|
||||
"^(hover|active):", # pseudo classes
|
||||
]
|
||||
for pattern in utility_patterns:
|
||||
|
||||
Reference in New Issue
Block a user