From 68e05d4de091ce82da3b46c11610bdaff141867d Mon Sep 17 00:00:00 2001 From: Amos Date: Fri, 5 Apr 2024 12:26:04 +0800 Subject: [PATCH 1/5] Feat: Tilt.lightShadowMode and configuration --- CHANGELOG.md | 6 + README-ZH.md | 25 ++- README.md | 25 ++- lib/src/config/tilt_light_config.dart | 22 ++- lib/src/config/tilt_shadow_config.dart | 101 +++++++++- lib/src/enums.dart | 38 ++++ lib/src/tilt.dart | 5 + lib/src/widget/tilt_container.dart | 248 ++++++++++++++++++------- lib/src/widget/tilt_shadow.dart | 170 ++++++++++++----- pubspec.yaml | 2 +- 10 files changed, 524 insertions(+), 118 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b41e8b6..93d8fae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ > [!IMPORTANT] > See the [Migration Guide](guides/migration_guide.md) for the details of breaking changes between versions. +## 3.1.0 + +### New features + +- Add `Tilt.lightShadowMode` and configuration. + ## 3.0.0 ### Breaking changes diff --git a/README-ZH.md b/README-ZH.md index f9ea228..e31dbdf 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -79,6 +79,8 @@ - [TiltConfig][] + - [LightShadowMode][] + - [LightConfig][] - [ShadowConfig][] @@ -227,6 +229,7 @@ Tilt( | borderRadius | `BorderRadiusGeometry?` | `null` | BoxDecoration borderRadius。 | | clipBehavior | `Clip` | `Clip.antiAlias` | Flutter 中的 clipBehavior。 | | tiltConfig | [TiltConfig][] | `TiltConfig()` | 倾斜效果配置。 | +| lightShadowMode | [LightShadowMode][] | `LightShadowMode.base` | 光影 Mode。 | | lightConfig | [LightConfig][] | `LightConfig()` | 光照效果配置。 | | shadowConfig | [ShadowConfig][] | `ShadowConfig()` | 阴影效果配置。 | | onGestureMove | `void Function(TiltDataModel, GesturesType)?` | `null` | 手势移动的回调触发。 | @@ -309,6 +312,14 @@ tiltStreamController.add( | controllerLeaveDuration | `Duration` | `Duration(milliseconds: 300)` | controller 手势离开后的动画持续时间。
仅以下手势生效:
`GesturesType.controller` | +### LightShadowMode 📄 + +| 枚举值 | 描述 | +| --- | --- | +| LightShadowMode.base | `[无性能风险]`
仅对 `Tilt.child` 使用普通阴影效果,无性能损耗。 | +| LightShadowMode.projector | `[有性能风险]`
对整个 `Tilt` widget 使用类似投影仪效果的阴影,
会显示与 widget 非透明部分完全一致的阴影。
建议在以下场景使用:
- 仅图片
- 无数据状态
- 无 Hero 标签 | + + ### LightConfig 📄 | 参数名 | 类型 | 默认值 | 描述 | @@ -318,6 +329,7 @@ tiltStreamController.add( | minIntensity | `double` | `0.0` | 颜色最小不透明度,也是初始不透明度。 | | maxIntensity | `double` | `0.5` | 颜色最大不透明度,跟随倾斜最大进度。 | | spreadFactor | `double` | `4.0` | 光源扩散系数,相对于当前 widget 尺寸。 | +| projectorScale | `double` | `1.1` | 光照区域尺寸比例。
仅以下模式生效:
`[LightShadowMode.projector]` | | direction | `LightDirection` | `LightDirection.around` | 光照方向。
影响:
`[ShadowConfig.direction]`(配置后不受影响)。 | | enableReverse | `bool` | `false` | 反转光照方向。
影响:
`[ShadowConfig.direction]`(配置后不受影响)。
`[ShadowConfig.enableReverse]`(配置后不受影响)。 | @@ -332,10 +344,14 @@ tiltStreamController.add( | maxIntensity | `double` | `0.5` | 颜色最大不透明度,跟随倾斜最大进度。 | | offsetInitial | `Offset` | `Offset(0.0, 0.0)` | 阴影偏移初始值。
例如:(0.0, 0.0) 中心
(40.0, 40.0) 向左上角偏移 40。 | | offsetFactor | `double` | `0.1` | 阴影偏移系数,相对于当前 widget 尺寸。 | -| spreadInitial | `double` | `0.0` | 阴影扩散半径初始值。 | -| spreadFactor | `double` | `0.0` | 阴影扩散半径系数,相对于当前 widget 尺寸。 | -| minBlurRadius | `double` | `10.0` | 最小阴影模糊半径,也是初始模糊半径。 | -| maxBlurRadius | `double` | `20.0` | 最大阴影模糊半径,跟随倾斜最大进度。 | +| spreadInitial | `double` | `0.0` | 阴影扩散半径初始值。
仅以下模式生效:
`[LightShadowMode.base]` | +| spreadFactor | `double` | `0.0` | 阴影扩散半径系数,相对于当前 widget 尺寸。
仅以下模式生效:
`[LightShadowMode.base]` | +| minBlurRadius | `double` | `10.0` | 最小阴影模糊半径,也是初始模糊半径。
仅以下模式生效:
`[LightShadowMode.base]` | +| maxBlurRadius | `double` | `20.0` | 最大阴影模糊半径,跟随倾斜最大进度。
仅以下模式生效:
`[LightShadowMode.base]` | +| projectorScaleFrom | `double` | `1.0` | 最小倾斜进度的阴影尺寸比例,也是初始尺寸比例。
仅以下模式生效:
`[LightShadowMode.projector]` | +| projectorScaleTo | `double` | `1.0` | 最大倾斜进度的阴影尺寸比例。
仅以下模式生效:
`[LightShadowMode.projector]` | +| projectorBlurSigmaFrom | `double` | `5.0` | 最小倾斜进度的阴影模糊 Sigma,也是初始模糊 Sigma。
仅以下模式生效:
`[LightShadowMode.projector]` | +| projectorBlurSigmaTo | `double` | `10.0` | 最大倾斜进度的阴影模糊 Sigma。
仅以下模式生效:
`[LightShadowMode.projector]` | | direction | `ShadowDirection?` | `null` | 阴影方向。 | | enableReverse | `bool?` | `null` | 反转阴影方向。 | @@ -371,6 +387,7 @@ tiltStreamController.add( [ChildLayout]: #childlayout- [StreamController<TiltStreamModel>]: #streamcontrollertiltstreammodel- [TiltConfig]: #tiltconfig- +[LightShadowMode]: #lightshadowmode- [LightConfig]: #lightconfig- [ShadowConfig]: #shadowconfig- [Gyroscope Browser compatibility]: https://developer.mozilla.org/en-US/docs/Web/API/Sensor_APIs#api.gyroscope diff --git a/README.md b/README.md index b9aa062..5c280c6 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,8 @@ Check out the [Live Demo][]. - [TiltConfig][] + - [LightShadowMode][] + - [LightConfig][] - [ShadowConfig][] @@ -227,6 +229,7 @@ Tilt( | borderRadius | `BorderRadiusGeometry?` | `null` | BoxDecoration borderRadius. | | clipBehavior | `Clip` | `Clip.antiAlias` | Flutter clipBehavior. | | tiltConfig | [TiltConfig][] | `TiltConfig()` | Tilt effect config. | +| lightShadowMode | [LightShadowMode][] | `LightShadowMode.base` | Light & Shadow Mode. | | lightConfig | [LightConfig][] | `LightConfig()` | Light effect config. | | shadowConfig | [ShadowConfig][] | `ShadowConfig()` | Shadow effect config. | | onGestureMove | `void Function(TiltDataModel, GesturesType)?` | `null` | Gesture move callback. | @@ -309,6 +312,14 @@ tiltStreamController.add( | controllerLeaveDuration | `Duration` | `Duration(milliseconds: 300)` | Animation duration after controller gesture leave.
Only the following gestures:
`GesturesType.controller` | +### LightShadowMode 📄 + +| Enum Value | Description | +| --- | --- | +| LightShadowMode.base | `[No performance risk]`
Use normal shadow effects only for `Tilt.child` without performance degradation. | +| LightShadowMode.projector | `[Performance risk exists]`
Apply a shadow to the entire `Tilt` widget, similar to a projector effect.
will display a shadow that exactly matches the non-transparent parts of the widget.
Recommended for the following scenarios:
- Images only
- No data states
- No Hero tags | + + ### LightConfig 📄 | Parameter | Type | Default | Description | @@ -318,6 +329,7 @@ tiltStreamController.add( | minIntensity | `double` | `0.0` | Color minimum opacity, also initial opacity. | | maxIntensity | `double` | `0.5` | Color maximum opacity for tilt progresses. | | spreadFactor | `double` | `4.0` | Light spread factor, relative to current widget size. | +| projectorScale | `double` | `1.1` | Light area size scale.
Only the following mode:
`[LightShadowMode.projector]` | | direction | `LightDirection` | `LightDirection.around` | Light direction.
Affects:
`[ShadowConfig.direction]` (not affected after configuration). | | enableReverse | `bool` | `false` | Reverse light direction.
Affects:
`[ShadowConfig.direction]` (not affected after configuration).
`[ShadowConfig.enableReverse]` (not affected after configuration). | @@ -332,10 +344,14 @@ tiltStreamController.add( | maxIntensity | `double` | `0.5` | Color maximum opacity as tilt progresses. | | offsetInitial | `Offset` | `Offset(0.0, 0.0)` | Initial value of shadow offset.
e.g. (0.0, 0.0) center.
(40.0, 40.0) Offset 40 to the top left. | | offsetFactor | `double` | `0.1` | Shadow offset factor, relative to current widget size. | -| spreadInitial | `double` | `0.0` | Initial value of shadow spread radius. | -| spreadFactor | `double` | `0.0` | Shadow spread radius factor, relative to current widget size. | -| minBlurRadius | `double` | `10.0` | Minimum blur radius, also initial blur radius. | -| maxBlurRadius | `double` | `20.0` | Maximum blur radius for tilt progresses. | +| spreadInitial | `double` | `0.0` | Initial value of shadow spread radius.
Only the following mode:
`[LightShadowMode.base]` | +| spreadFactor | `double` | `0.0` | Shadow spread radius factor, relative to current widget size.
Only the following mode:
`[LightShadowMode.base]` | +| minBlurRadius | `double` | `10.0` | Minimum blur radius, also initial blur radius.
Only the following mode:
`[LightShadowMode.base]` | +| maxBlurRadius | `double` | `20.0` | Maximum blur radius for tilt progresses.
Only the following mode:
`[LightShadowMode.base]` | +| projectorScaleFrom | `double` | `1.0` | Size scale for minimum progress, also initial size scale.
Only the following mode:
`[LightShadowMode.projector]` | +| projectorScaleTo | `double` | `1.0` | Size scale for maximum progress.
Only the following mode:
`[LightShadowMode.projector]` | +| projectorBlurSigmaFrom | `double` | `5.0` | Blur sigma for minimum progress, also initial blur sigma.
Only the following mode:
`[LightShadowMode.projector]` | +| projectorBlurSigmaTo | `double` | `10.0` | Blur sigma for maximum progress.
Only the following mode:
`[LightShadowMode.projector]` | | direction | `ShadowDirection?` | `null` | Shadow direction. | | enableReverse | `bool?` | `null` | Reverse shadow direction. | @@ -371,6 +387,7 @@ Open sourced under the MIT license. [ChildLayout]: #childlayout- [StreamController<TiltStreamModel>]: #streamcontrollertiltstreammodel- [TiltConfig]: #tiltconfig- +[LightShadowMode]: #lightshadowmode- [LightConfig]: #lightconfig- [ShadowConfig]: #shadowconfig- [Gyroscope Browser compatibility]: https://developer.mozilla.org/en-US/docs/Web/API/Sensor_APIs#api.gyroscope diff --git a/lib/src/config/tilt_light_config.dart b/lib/src/config/tilt_light_config.dart index 7d4510b..44a9aa6 100644 --- a/lib/src/config/tilt_light_config.dart +++ b/lib/src/config/tilt_light_config.dart @@ -20,6 +20,7 @@ class LightConfig { this.minIntensity = 0.0, this.maxIntensity = 0.5, this.spreadFactor = 4.0, + this.projectorScale = 1.1, this.direction = LightDirection.around, this.enableReverse, }) : assert( @@ -27,7 +28,8 @@ class LightConfig { minIntensity >= 0.0 && maxIntensity <= 1.0, ), - assert(spreadFactor >= 1.0); + assert(spreadFactor >= 1.0), + assert(projectorScale >= 0.0); /// Only disable the light effect. /// @@ -66,6 +68,20 @@ class LightConfig { /// 相对于当前 widget 尺寸。 final double spreadFactor; + /// Light area size scale + /// + /// Only the following mode: + /// [LightShadowMode.projector] + /// + /// ------ + /// + /// 光照区域尺寸比例 + /// + /// 仅以下模式生效: + /// [LightShadowMode.projector] + /// + final double projectorScale; + /// Light direction. /// /// Affects: @@ -108,6 +124,7 @@ class LightConfig { double? minIntensity, double? maxIntensity, double? spreadFactor, + double? projectorScale, LightDirection? direction, bool? enableReverse, }) { @@ -117,6 +134,7 @@ class LightConfig { minIntensity: minIntensity ?? this.minIntensity, maxIntensity: maxIntensity ?? this.maxIntensity, spreadFactor: spreadFactor ?? this.spreadFactor, + projectorScale: projectorScale ?? this.projectorScale, direction: direction ?? this.direction, enableReverse: enableReverse ?? this.enableReverse, ); @@ -136,6 +154,7 @@ class LightConfig { other.minIntensity == minIntensity && other.maxIntensity == maxIntensity && other.spreadFactor == spreadFactor && + other.projectorScale == projectorScale && other.direction == direction && other.enableReverse == enableReverse; } @@ -148,6 +167,7 @@ class LightConfig { minIntensity, maxIntensity, spreadFactor, + projectorScale, direction, enableReverse, ); diff --git a/lib/src/config/tilt_shadow_config.dart b/lib/src/config/tilt_shadow_config.dart index 8513c33..1ec7266 100644 --- a/lib/src/config/tilt_shadow_config.dart +++ b/lib/src/config/tilt_shadow_config.dart @@ -30,6 +30,10 @@ class ShadowConfig { this.spreadFactor = 0.0, this.minBlurRadius = 10.0, this.maxBlurRadius = 20.0, + this.projectorScaleFrom = 1.0, + this.projectorScaleTo = 1.0, + this.projectorBlurSigmaFrom = 2.0, + this.projectorBlurSigmaTo = 10.0, this.direction, this.enableReverse, }) : assert( @@ -39,7 +43,9 @@ class ShadowConfig { ), assert(offsetFactor >= 0.0), assert(spreadFactor >= 0.0), - assert(minBlurRadius <= maxBlurRadius && minBlurRadius >= 0.0); + assert(minBlurRadius <= maxBlurRadius && minBlurRadius >= 0.0), + assert(projectorScaleFrom >= 0 && projectorScaleTo >= 0), + assert(projectorBlurSigmaFrom >= 0.0 && projectorBlurSigmaTo >= 0.0); /// Only disable the shadow effect. /// @@ -98,36 +104,112 @@ class ShadowConfig { /// Initial value of shadow spread radius. /// + /// Only the following mode: + /// [LightShadowMode.base] + /// /// ------ /// /// 阴影扩散半径初始值。 + /// + /// 仅以下模式生效: + /// [LightShadowMode.base] final double spreadInitial; /// Shadow spread radius factor, /// relative to current widget size. /// + /// Only the following mode: + /// [LightShadowMode.base] + /// /// ------ /// /// 阴影扩散半径系数, /// 相对于当前 widget 尺寸。 /// /// 移动时相对当前的尺寸进行扩散 + /// + /// 仅以下模式生效: + /// [LightShadowMode.base] final double spreadFactor; /// Minimum blur radius, also initial blur radius. /// + /// Only the following mode: + /// [LightShadowMode.base] + /// /// ------ /// /// 最小阴影模糊半径,也是初始模糊半径。 + /// + /// 仅以下模式生效: + /// [LightShadowMode.base] final double minBlurRadius; /// Maximum blur radius for tilt progresses. /// + /// Only the following mode: + /// [LightShadowMode.base] + /// /// ------ /// /// 最大阴影模糊半径,跟随倾斜最大进度。 + /// + /// 仅以下模式生效: + /// [LightShadowMode.base] final double maxBlurRadius; + /// Size scale for minimum progress, also initial size scale. + /// + /// Only the following mode: + /// [LightShadowMode.projector] + /// + /// ------ + /// + /// 最小倾斜进度的阴影尺寸比例,也是初始尺寸比例。 + /// + /// 仅以下模式生效: + /// [LightShadowMode.projector] + final double projectorScaleFrom; + + /// Size scale for maximum progress. + /// + /// Only the following mode: + /// [LightShadowMode.projector] + /// + /// ------ + /// + /// 最大倾斜进度的阴影尺寸比例。 + /// + /// 仅以下模式生效: + /// [LightShadowMode.projector] + final double projectorScaleTo; + + /// Blur sigma for minimum progress, also initial blur sigma. + /// + /// Only the following mode: + /// [LightShadowMode.projector] + /// + /// ------ + /// + /// 最小倾斜进度的阴影模糊 Sigma,也是初始模糊 Sigma。 + /// + /// 仅以下模式生效: + /// [LightShadowMode.projector] + final double projectorBlurSigmaFrom; + + /// Blur sigma for maximum progress. + /// + /// Only the following mode: + /// [LightShadowMode.projector] + /// + /// ------ + /// + /// 最大倾斜进度的阴影模糊 Sigma。 + /// + /// 仅以下模式生效: + /// [LightShadowMode.projector] + final double projectorBlurSigmaTo; + /// Shadow direction. /// 阴影方向。 /// @@ -158,6 +240,10 @@ class ShadowConfig { double? spreadFactor, double? minBlurRadius, double? maxBlurRadius, + double? projectorScaleFrom, + double? projectorScaleTo, + double? projectorBlurSigmaFrom, + double? projectorBlurSigmaTo, ShadowDirection? direction, bool? enableReverse, }) { @@ -172,6 +258,11 @@ class ShadowConfig { spreadFactor: spreadFactor ?? this.spreadFactor, minBlurRadius: minBlurRadius ?? this.minBlurRadius, maxBlurRadius: maxBlurRadius ?? this.maxBlurRadius, + projectorScaleFrom: projectorScaleFrom ?? this.projectorScaleFrom, + projectorScaleTo: projectorScaleTo ?? this.projectorScaleTo, + projectorBlurSigmaFrom: + projectorBlurSigmaFrom ?? this.projectorBlurSigmaFrom, + projectorBlurSigmaTo: projectorBlurSigmaTo ?? this.projectorBlurSigmaTo, direction: direction ?? this.direction, enableReverse: enableReverse ?? this.enableReverse, ); @@ -196,6 +287,10 @@ class ShadowConfig { other.spreadFactor == spreadFactor && other.minBlurRadius == minBlurRadius && other.maxBlurRadius == maxBlurRadius && + other.projectorScaleFrom == projectorScaleFrom && + other.projectorScaleTo == projectorScaleTo && + other.projectorBlurSigmaFrom == projectorBlurSigmaFrom && + other.projectorBlurSigmaTo == projectorBlurSigmaTo && other.direction == direction && other.enableReverse == enableReverse; } @@ -213,6 +308,10 @@ class ShadowConfig { spreadFactor, minBlurRadius, maxBlurRadius, + projectorScaleFrom, + projectorScaleTo, + projectorBlurSigmaFrom, + projectorBlurSigmaTo, direction, enableReverse, ); diff --git a/lib/src/enums.dart b/lib/src/enums.dart index a9ed29a..7cc4f3c 100644 --- a/lib/src/enums.dart +++ b/lib/src/enums.dart @@ -1,3 +1,41 @@ +/// Light & Shadow Mode +/// 光影 Mode +enum LightShadowMode { + /// [No performance risk] + /// + /// Use normal shadow effects only for `Tilt.child` without performance degradation. + /// + /// ------ + /// + /// [无性能风险] + /// + /// 仅对 `Tilt.child` 使用普通阴影效果,无性能损耗。 + base, + + /// [Performance risk exists] + /// + /// Apply a shadow to the entire `Tilt` widget, similar to a projector effect. + /// will display a shadow that exactly matches the non-transparent parts of the widget. + /// + /// Recommended for the following scenarios: + /// - Images only + /// - No data states + /// - No Hero tags + /// + /// ------ + /// + /// [有性能风险] + /// + /// 对整个 `Tilt` widget 使用类似投影仪效果的阴影, + /// 会显示与 widget 非透明部分完全一致的阴影。 + /// + /// 建议在以下场景使用: + /// - 仅图片 + /// - 无数据状态 + /// - 无 Hero 标签 + projector, +} + /// Light direction. /// 光照方向。 /// diff --git a/lib/src/tilt.dart b/lib/src/tilt.dart index 70130e9..fb3cac2 100644 --- a/lib/src/tilt.dart +++ b/lib/src/tilt.dart @@ -27,6 +27,7 @@ class Tilt extends TiltContainer { /// - [borderRadius] : BoxDecoration borderRadius. /// - [clipBehavior] : Flutter clipBehavior. /// - [tiltConfig] : Tilt effect config. + /// - [lightShadowMode] : Light & Shadow Mode /// - [lightConfig] : Light effect config. /// - [shadowConfig] : Shadow effect config. /// - [onGestureMove] : Gesture move callback. @@ -42,6 +43,7 @@ class Tilt extends TiltContainer { /// - [borderRadius] : BoxDecoration 边框圆角半径。 /// - [clipBehavior] : Flutter clipBehavior。 /// - [tiltConfig] : 倾斜效果配置。 + /// - [lightShadowMode] : 光影 Mode /// - [lightConfig] : 光照效果配置。 /// - [shadowConfig] : 阴影效果配置。 /// - [onGestureMove] : 手势移动的回调触发。 @@ -58,6 +60,7 @@ class Tilt extends TiltContainer { super.borderRadius, super.clipBehavior = Clip.antiAlias, super.tiltConfig = const TiltConfig(), + super.lightShadowMode, super.lightConfig = const LightConfig(), super.shadowConfig = const ShadowConfig(), this.onGestureMove, @@ -117,6 +120,7 @@ class _TiltState extends State { BorderRadiusGeometry? get _borderRadius => widget.borderRadius; Clip get _clipBehavior => widget.clipBehavior; TiltConfig get _tiltConfig => widget.tiltConfig; + LightShadowMode get _lightShadowMode => widget.lightShadowMode; LightConfig get _lightConfig => widget.lightConfig; ShadowConfig get _shadowConfig => widget.shadowConfig; TiltCallback? get _onGestureMove => widget.onGestureMove; @@ -199,6 +203,7 @@ class _TiltState extends State { borderRadius: _borderRadius, clipBehavior: _clipBehavior, tiltConfig: _tiltConfig, + lightShadowMode: _lightShadowMode, lightConfig: _lightConfig, shadowConfig: _shadowConfig, childLayout: _childLayout, diff --git a/lib/src/widget/tilt_container.dart b/lib/src/widget/tilt_container.dart index 07691c5..adcfc37 100644 --- a/lib/src/widget/tilt_container.dart +++ b/lib/src/widget/tilt_container.dart @@ -19,6 +19,7 @@ class TiltContainer extends StatefulWidget { this.borderRadius, required this.clipBehavior, required this.tiltConfig, + this.lightShadowMode = LightShadowMode.base, required this.lightConfig, required this.shadowConfig, }); @@ -57,6 +58,13 @@ class TiltContainer extends StatefulWidget { /// 倾斜效果配置。 final TiltConfig tiltConfig; + /// Light & Shadow Mode. + /// + /// ------ + /// + /// 光影模式。 + final LightShadowMode lightShadowMode; + /// Light effect config. /// /// ------ @@ -82,6 +90,7 @@ class _TiltContainerState extends State with TiltTweenAnimation { BorderRadiusGeometry? get _borderRadius => widget.borderRadius; Clip get _clipBehavior => widget.clipBehavior; TiltConfig get _tiltConfig => widget.tiltConfig; + LightShadowMode get _lightShadowMode => widget.lightShadowMode; LightConfig get _lightConfig => widget.lightConfig; ShadowConfig get _shadowConfig => widget.shadowConfig; @@ -140,75 +149,186 @@ class _TiltContainerState extends State with TiltTweenAnimation { child: Stack( alignment: AlignmentDirectional.center, clipBehavior: Clip.none, - children: [ - /// behind child - ..._childLayout.behind, - - /// main child - TiltShadow( - width: width, - height: height, - areaProgress: value, - border: _border, - borderRadius: _borderRadius, - clipBehavior: _clipBehavior, - lightConfig: _lightConfig, - shadowConfig: _shadowConfig, - child: Stack( - alignment: AlignmentDirectional.center, - - /// 避免暴露其他组件,[Clip.none] 时,默认赋值 [Clip.hardEdge] - clipBehavior: _clipBehavior == Clip.none - ? Clip.hardEdge - : _clipBehavior, - children: [ - /// body - Container( - decoration: BoxDecoration( - borderRadius: _borderRadius, - ), - clipBehavior: _clipBehavior, - child: child, - ), - - /// inner child - ..._childLayout.inner, - - /// light - TiltLight( - width: width, - height: height, - areaProgress: value, - lightConfig: _lightConfig, - ), - - /// resize - Positioned.fill( - child: LayoutBuilder( - builder: ( - BuildContext context, - BoxConstraints constraints, - ) { - WidgetsBinding.instance.endOfFrame.then((_) { - if (mounted) { - tiltState.onResize(constraints.biggest); - } - }); - return const SizedBox(); - }, - ), - ), - ], + children: switch (_lightShadowMode) { + LightShadowMode.base => lightShadowModeBase( + child: child, + areaProgress: value, ), - ), - - /// outer child - ..._childLayout.outer, - ], + LightShadowMode.projector => lightShadowModeProjector( + child: child, + areaProgress: value, + ), + }, ), ); }, child: _child, ); } + + /// Widget Resize + Widget widgetResize() { + return Positioned.fill( + child: LayoutBuilder( + builder: ( + BuildContext context, + BoxConstraints constraints, + ) { + WidgetsBinding.instance.endOfFrame.then((_) { + if (mounted) { + tiltState.onResize(constraints.biggest); + } + }); + return const SizedBox(); + }, + ), + ); + } + + /// Stack Inner + Widget stackInner(List children) { + return Stack( + alignment: AlignmentDirectional.center, + + /// 避免暴露其他组件,[Clip.none] 时,默认赋值 [Clip.hardEdge] + clipBehavior: _clipBehavior == Clip.none ? Clip.hardEdge : _clipBehavior, + children: children, + ); + } + + /// LightShadowMode - Base + List lightShadowModeBase({ + required Widget? child, + required Offset areaProgress, + }) { + return [ + /// behind child + ..._childLayout.behind, + + /// main child + TiltShadowBase( + width: width, + height: height, + areaProgress: areaProgress, + border: _border, + borderRadius: _borderRadius, + clipBehavior: _clipBehavior, + lightConfig: _lightConfig, + shadowConfig: _shadowConfig, + child: stackInner([ + /// body + child ?? const SizedBox(), + + /// inner child + ..._childLayout.inner, + + /// light + TiltLight( + width: width, + height: height, + areaProgress: areaProgress, + lightConfig: _lightConfig, + ), + + /// resize + widgetResize(), + ]), + ), + + /// outer child + ..._childLayout.outer, + ]; + } + + /// LightShadowMode - Projector + List lightShadowModeProjector({ + required Widget? child, + required Offset areaProgress, + }) { + return [ + /// shadow + TiltShadowProjector( + width: width, + height: height, + areaProgress: areaProgress, + lightConfig: _lightConfig, + shadowConfig: _shadowConfig, + child: Stack( + alignment: AlignmentDirectional.center, + clipBehavior: Clip.none, + children: [ + /// behind child + ..._childLayout.behind, + + /// main child + Container( + decoration: BoxDecoration( + border: _border, + borderRadius: _borderRadius, + ), + clipBehavior: _clipBehavior, + child: stackInner([ + /// body + child ?? const SizedBox(), + + /// inner child + ..._childLayout.inner, + ]), + ), + + /// outer child + ..._childLayout.outer, + ], + ), + ), + + /// behind child + ..._childLayout.behind, + + /// main child + Container( + decoration: BoxDecoration( + border: _border, + borderRadius: _borderRadius, + ), + clipBehavior: _clipBehavior, + child: stackInner([ + /// body + child ?? const SizedBox(), + + /// inner child + ..._childLayout.inner, + + /// resize + widgetResize(), + ]), + ), + + /// outer child + ..._childLayout.outer, + + /// light + IgnorePointer( + child: Transform.scale( + scale: _lightConfig.projectorScale, + child: Container( + width: width, + height: height, + decoration: BoxDecoration( + borderRadius: _borderRadius, + ), + clipBehavior: _clipBehavior, + child: stackInner([ + TiltLight( + width: width, + height: height, + areaProgress: areaProgress, + lightConfig: _lightConfig, + ), + ]), + ), + ), + ), + ]; + } } diff --git a/lib/src/widget/tilt_shadow.dart b/lib/src/widget/tilt_shadow.dart index 1cfbbe3..63e991e 100644 --- a/lib/src/widget/tilt_shadow.dart +++ b/lib/src/widget/tilt_shadow.dart @@ -1,3 +1,4 @@ +import 'dart:ui'; import 'package:flutter/widgets.dart'; import '../utils.dart'; @@ -6,24 +7,13 @@ import '../config/tilt_light_config.dart'; import '../config/tilt_shadow_config.dart'; import '../internal/tilt_decoration_mixin.dart'; -/// 阴影 -class TiltShadow extends StatelessWidget with TiltDecoration { - /// 阴影 - /// - /// 作用于其他组件上的阴影效果 - /// - /// [width], [height] 一般和传入的组件尺寸一致 - /// - /// {@macro tilt.ShadowConfig} +abstract class TiltShadow extends StatelessWidget with TiltDecoration { const TiltShadow({ super.key, required this.child, required this.width, required this.height, required this.areaProgress, - this.border, - this.borderRadius, - required this.clipBehavior, required this.lightConfig, required this.shadowConfig, }); @@ -34,30 +24,12 @@ class TiltShadow extends StatelessWidget with TiltDecoration { /// 当前坐标的区域进度 final Offset areaProgress; - /// Border - final BoxBorder? border; - - /// BorderRadius - final BorderRadiusGeometry? borderRadius; - - /// Clip - final Clip clipBehavior; - /// 光源配置 final LightConfig lightConfig; /// 阴影配置 final ShadowConfig shadowConfig; - /// 当前坐标的区域进度 - Offset get progress => -areaProgress; - - /// 距离中心的进度 - double get centerProgress => Utils.p2pDistance(Offset.zero, progress); - - /// 距离中心的进度最大值 - double get centerMaxProgress => centerProgress > 1.0 ? 1.0 : centerProgress; - /// 阴影显示(受光源影响) /// /// 用于阴影颜色,限制最大进度表示强度(透明度) @@ -70,6 +42,28 @@ class TiltShadow extends StatelessWidget with TiltDecoration { max: shadowConfig.maxIntensity, ); + /// 开启反向(受光源影响) + /// + /// {@macro tilt.ShadowConfig.enableReverse} + bool get enableReverse => + shadowConfig.enableReverse ?? + (shadowConfig.direction == null && (lightConfig.enableReverse == true)); + + /// 禁用阴影 + bool get shadowDisable => + shadowConfig.disable || + shadowConfig.maxIntensity == 0.0 || + shadowConfig.direction == ShadowDirection.none; + + /// 当前坐标的区域进度 + Offset get progress => -areaProgress; + + /// 距离中心的进度 + double get centerProgress => Utils.p2pDistance(Offset.zero, progress); + + /// 距离中心的进度最大值 + double get centerMaxProgress => centerProgress > 1.0 ? 1.0 : centerProgress; + /// 阴影当前偏移距离 /// /// 阴影进度 * 阴影偏移系数的距离(相对当前尺寸的中心) @@ -83,6 +77,39 @@ class TiltShadow extends StatelessWidget with TiltDecoration { /// 阴影偏移 Offset get offset => (enableReverse ? -baseOffset : baseOffset) - shadowConfig.offsetInitial; +} + +/// 阴影 Base +/// [LightShadowMode.base] +class TiltShadowBase extends TiltShadow { + /// 阴影 Base + /// + /// 作用于其他组件上的阴影效果 + /// + /// [width], [height] 一般和传入的组件尺寸一致 + /// + /// {@macro tilt.ShadowConfig} + const TiltShadowBase({ + super.key, + required super.child, + required super.width, + required super.height, + required super.areaProgress, + required super.lightConfig, + required super.shadowConfig, + this.border, + this.borderRadius, + required this.clipBehavior, + }); + + /// Border + final BoxBorder? border; + + /// BorderRadius + final BorderRadiusGeometry? borderRadius; + + /// Clip + final Clip clipBehavior; /// 阴影模糊半径进度 /// @@ -118,19 +145,6 @@ class TiltShadow extends StatelessWidget with TiltDecoration { double get spreadRadius => spreadRadiusDistance - spreadRadiusRevert + shadowConfig.spreadInitial; - /// 开启反向(受光源影响) - /// - /// {@macro tilt.ShadowConfig.enableReverse} - bool get enableReverse => - shadowConfig.enableReverse ?? - (shadowConfig.direction == null && (lightConfig.enableReverse == true)); - - /// 禁用阴影 - bool get shadowDisable => - shadowConfig.disable || - shadowConfig.maxIntensity == 0.0 || - shadowConfig.direction == ShadowDirection.none; - @override Widget build(BuildContext context) { return Container( @@ -142,6 +156,7 @@ class TiltShadow extends StatelessWidget with TiltDecoration { offset: offset, blurRadius: blurRadius, spreadRadius: spreadRadius, + blurStyle: BlurStyle.normal, ), ], border: border, @@ -152,3 +167,72 @@ class TiltShadow extends StatelessWidget with TiltDecoration { ); } } + +/// 阴影 Projector +/// [LightShadowMode.projector] +class TiltShadowProjector extends TiltShadow { + /// 阴影 Projector + /// + /// 作用于其他组件上的阴影效果 + /// + /// [width], [height] 一般和传入的组件尺寸一致 + /// + /// {@macro tilt.ShadowConfig} + const TiltShadowProjector({ + super.key, + required super.child, + required super.width, + required super.height, + required super.areaProgress, + required super.lightConfig, + required super.shadowConfig, + }); + + /// 阴影尺寸比例 + double get scale => shadowConfig.projectorScaleTo >= + shadowConfig.projectorScaleFrom + ? shadowConfig.projectorScaleFrom + + centerMaxProgress * + (shadowConfig.projectorScaleTo - shadowConfig.projectorScaleFrom) + : shadowConfig.projectorScaleFrom - + centerMaxProgress * + (shadowConfig.projectorScaleFrom - shadowConfig.projectorScaleTo); + + /// 阴影模糊 Sigma + double get blurSigma => + shadowConfig.projectorBlurSigmaTo >= shadowConfig.projectorBlurSigmaFrom + ? shadowConfig.projectorBlurSigmaFrom + + centerMaxProgress * + (shadowConfig.projectorBlurSigmaTo - + shadowConfig.projectorBlurSigmaFrom) + : shadowConfig.projectorBlurSigmaFrom - + centerMaxProgress * + (shadowConfig.projectorBlurSigmaFrom - + shadowConfig.projectorBlurSigmaTo); + + @override + Widget build(BuildContext context) { + return IgnorePointer( + child: Transform.translate( + offset: offset, + child: ImageFiltered( + imageFilter: ImageFilter.blur( + sigmaX: blurSigma, + sigmaY: blurSigma, + tileMode: TileMode.decal, + ), + child: Opacity( + opacity: showShadow, + child: ColorFiltered( + colorFilter: ColorFilter.mode( + shadowConfig.color, + BlendMode.srcATop, + ), + child: Transform.scale(scale: scale, child: child), + ), + ), + ), + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 38c0752..80f8491 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,7 @@ description: Easily apply tilt parallax hover effects for Flutter, which support # https://semver.org/spec/v2.0.0-rc.1.html # https://dart.dev/tools/pub/versioning#semantic-versions # https://dart.dev/tools/pub/dependencies#version-constraints -version: 3.0.0 +version: 3.1.0 homepage: https://amoshuke.github.io/flutter_tilt_book repository: https://github.com/fluttercandies/flutter_tilt issue_tracker: https://github.com/fluttercandies/flutter_tilt/issues From bea7ef32e05158a3be163d690dbeccdc5821985a Mon Sep 17 00:00:00 2001 From: Amos Date: Fri, 5 Apr 2024 12:26:51 +0800 Subject: [PATCH 2/5] Test: LightShadowMode --- test/config/tilt_light_config_test.dart | 6 ++ test/config/tilt_shadow_config_test.dart | 22 +++++++ test/tilt_widget/tilt_gestures_drag_test.dart | 63 +++++++++++++++++++ .../tilt_widget/tilt_gestures_hover_test.dart | 41 ++++++++++++ .../tilt_widget/tilt_gestures_touch_test.dart | 58 +++++++++++++++++ test/tilt_widget/tilt_widget.dart | 3 + 6 files changed, 193 insertions(+) diff --git a/test/config/tilt_light_config_test.dart b/test/config/tilt_light_config_test.dart index d1a4591..8ea0c15 100644 --- a/test/config/tilt_light_config_test.dart +++ b/test/config/tilt_light_config_test.dart @@ -31,6 +31,10 @@ void main() { () => LightConfig(spreadFactor: 0.1), throwsAssertionError, ); + expect( + () => LightConfig(projectorScale: -1), + throwsAssertionError, + ); }); test('copyWith', () { const LightConfig lightConfig = LightConfig(); @@ -40,6 +44,7 @@ void main() { minIntensity: 0.0, maxIntensity: 0.5, spreadFactor: 1.0, + projectorScale: 2.0, direction: LightDirection.around, enableReverse: true, ); @@ -49,6 +54,7 @@ void main() { minIntensity: 0.0, maxIntensity: 0.5, spreadFactor: 1.0, + projectorScale: 2.0, direction: LightDirection.around, enableReverse: true, ); diff --git a/test/config/tilt_shadow_config_test.dart b/test/config/tilt_shadow_config_test.dart index 8e3a21b..e784b33 100644 --- a/test/config/tilt_shadow_config_test.dart +++ b/test/config/tilt_shadow_config_test.dart @@ -34,6 +34,20 @@ void main() { () => ShadowConfig(minBlurRadius: -0.1, maxBlurRadius: 1.0), throwsAssertionError, ); + expect( + () => ShadowConfig( + projectorScaleFrom: -0.1, + projectorScaleTo: -0.1, + ), + throwsAssertionError, + ); + expect( + () => ShadowConfig( + projectorBlurSigmaFrom: -0.1, + projectorBlurSigmaTo: -0.1, + ), + throwsAssertionError, + ); }); test('copyWith', () { const ShadowConfig shadowConfig = ShadowConfig(); @@ -48,6 +62,10 @@ void main() { spreadFactor: 6.0, minBlurRadius: 7.0, maxBlurRadius: 8.0, + projectorScaleFrom: 2.0, + projectorScaleTo: 2.0, + projectorBlurSigmaFrom: 2.0, + projectorBlurSigmaTo: 2.0, direction: ShadowDirection.all, enableReverse: true, ); @@ -62,6 +80,10 @@ void main() { spreadFactor: 6.0, minBlurRadius: 7.0, maxBlurRadius: 8.0, + projectorScaleFrom: 2.0, + projectorScaleTo: 2.0, + projectorBlurSigmaFrom: 2.0, + projectorBlurSigmaTo: 2.0, direction: ShadowDirection.all, enableReverse: true, ); diff --git a/test/tilt_widget/tilt_gestures_drag_test.dart b/test/tilt_widget/tilt_gestures_drag_test.dart index e4ea632..37e5602 100644 --- a/test/tilt_widget/tilt_gestures_drag_test.dart +++ b/test/tilt_widget/tilt_gestures_drag_test.dart @@ -59,5 +59,68 @@ void main() { expect(scrollController.offset, 100.0); expect(childFinder, findsNothing); }); + + testWidgets('LightShadowMode.projector scroll', + (WidgetTester tester) async { + final Finder childFinder = find.text('Tilt').first; + final Finder scrollFinder = find.byKey(const Key('scroll')); + final ScrollController scrollController = ScrollController(); + + /// 回调赋值 + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: ListView( + controller: scrollController, + children: const [ + Tilt( + key: Key('tilt_widget'), + lightShadowMode: LightShadowMode.projector, + shadowConfig: ShadowConfig( + projectorScaleFrom: 2, + projectorScaleTo: 1, + projectorBlurSigmaFrom: 2, + projectorBlurSigmaTo: 1, + ), + child: SizedBox(width: 100, height: 100, child: Text('Tilt')), + ), + SizedBox(key: Key('scroll'), height: 100, width: 100), + SizedBox(height: 1000), + ], + ), + ), + ), + ); + + /// onVerticalDragUpdate + await tester.timedDrag( + childFinder, + const Offset(0.0, -100.0), + const Duration(milliseconds: 1000), + ); + await tester.pumpAndSettle(); + expect(scrollController.offset, 0.0); + expect(childFinder, findsOneWidget); + + /// onHorizontalDragUpdate + await tester.timedDrag( + childFinder, + const Offset(-100.0, 0.0), + const Duration(milliseconds: 1000), + ); + await tester.pumpAndSettle(); + expect(scrollController.offset, 0.0); + expect(childFinder, findsOneWidget); + + /// scroll + await tester.timedDrag( + scrollFinder, + const Offset(0.0, -100.0), + const Duration(milliseconds: 1000), + warnIfMissed: false, + ); + await tester.pumpAndSettle(); + expect(scrollController.offset, 100.0); + }); }); } diff --git a/test/tilt_widget/tilt_gestures_hover_test.dart b/test/tilt_widget/tilt_gestures_hover_test.dart index ee3a2d6..d87d43c 100644 --- a/test/tilt_widget/tilt_gestures_hover_test.dart +++ b/test/tilt_widget/tilt_gestures_hover_test.dart @@ -583,6 +583,47 @@ void main() { expect(gesturesTypeTest, GesturesType.hover); expect(tiltDataTest, leaveTiltDataExpect, reason: '倾斜-超范围'); + await tester.sendEventToBinding(testPointer.removePointer()); + }); + testWidgets('LightShadowMode.projector gestures move leave', + (WidgetTester tester) async { + TiltDataModel? moveTiltDataTest; + TiltDataModel? leaveTiltDataTest; + GesturesType? moveGesturesTypeTest; + GesturesType? leaveGesturesTypeTest; + + /// 回调赋值 + await tester.pumpWidget( + TiltWidget( + lightShadowMode: LightShadowMode.projector, + onGestureMove: (TiltDataModel tiltData, GesturesType gesturesType) { + moveTiltDataTest = tiltData; + moveGesturesTypeTest = gesturesType; + }, + onGestureLeave: (TiltDataModel tiltData, GesturesType gesturesType) { + leaveTiltDataTest = tiltData; + leaveGesturesTypeTest = gesturesType; + }, + ), + ); + + /// mouse hover + final Offset hoverEventLocation = tester.getCenter(tiltWidgetFinder); + + /// 倾斜 hover move + await tester.sendEventToBinding(testPointer.hover(hoverEventLocation)); + await tester.pumpAndSettle(); + expect(childFinder, findsNWidgets(2)); + expect(moveGesturesTypeTest, GesturesType.hover); + expect(moveTiltDataTest != null, true); + + /// 倾斜 hover leave + await tester.sendEventToBinding(testPointer.hover(const Offset(-1, -1))); + await tester.pumpAndSettle(); + expect(childFinder, findsNWidgets(2)); + expect(leaveGesturesTypeTest, GesturesType.hover); + expect(leaveTiltDataTest, leaveTiltDataExpect); + await tester.sendEventToBinding(testPointer.removePointer()); }); }); diff --git a/test/tilt_widget/tilt_gestures_touch_test.dart b/test/tilt_widget/tilt_gestures_touch_test.dart index 7bbcb6e..85a5f2f 100644 --- a/test/tilt_widget/tilt_gestures_touch_test.dart +++ b/test/tilt_widget/tilt_gestures_touch_test.dart @@ -469,5 +469,63 @@ void main() { expect(gesturesTypeTest, GesturesType.touch); expect(tiltDataTest, tiltDataExpect, reason: '倾斜-超范围'); }); + + testWidgets('LightShadowMode.projector gestures move leave', + (WidgetTester tester) async { + TiltDataModel? moveTiltDataTest; + TiltDataModel? leaveTiltDataTest; + GesturesType? moveGesturesTypeTest; + GesturesType? leaveGesturesTypeTest; + final TiltDataModel leaveTiltDataTestExpect = TiltDataModel( + angle: Offset.zero, + areaProgress: Offset.zero, + position: const Offset(5.0, 5.0), + transform: Matrix4( + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.05, + 0.0, + 0.0, + 0.0, + 1.0, + ), + ); + + /// 回调赋值 + await tester.pumpWidget( + TiltWidget( + lightShadowMode: LightShadowMode.projector, + onGestureMove: (TiltDataModel tiltData, GesturesType gesturesType) { + moveTiltDataTest = tiltData; + moveGesturesTypeTest = gesturesType; + }, + onGestureLeave: (TiltDataModel tiltData, GesturesType gesturesType) { + leaveTiltDataTest = tiltData; + leaveGesturesTypeTest = gesturesType; + }, + ), + ); + + /// 倾斜 touch move + await tester.fling(tiltWidgetFinder, const Offset(0.0, 5.0), 0.1); + expect(childFinder, findsNWidgets(2)); + expect(moveGesturesTypeTest, GesturesType.touch); + expect(moveTiltDataTest != null, true); + + /// 倾斜 touch leave + await tester.fling(tiltScaffoldFinder, const Offset(0.0, -1.0), 0.1); + expect(childFinder, findsNWidgets(2)); + expect(leaveGesturesTypeTest, GesturesType.touch); + expect(leaveTiltDataTest, leaveTiltDataTestExpect); + }); }); } diff --git a/test/tilt_widget/tilt_widget.dart b/test/tilt_widget/tilt_widget.dart index add4dfb..ebf00f7 100644 --- a/test/tilt_widget/tilt_widget.dart +++ b/test/tilt_widget/tilt_widget.dart @@ -14,6 +14,7 @@ class TiltWidget extends StatelessWidget { this.borderRadius, this.clipBehavior = Clip.antiAlias, this.tiltConfig = const TiltConfig(), + this.lightShadowMode = LightShadowMode.base, this.lightConfig = const LightConfig(), this.shadowConfig = const ShadowConfig(), this.onGestureMove, @@ -28,6 +29,7 @@ class TiltWidget extends StatelessWidget { final BorderRadiusGeometry? borderRadius; final Clip clipBehavior; final TiltConfig tiltConfig; + final LightShadowMode lightShadowMode; final LightConfig lightConfig; final ShadowConfig shadowConfig; final void Function(TiltDataModel, GesturesType)? onGestureMove; @@ -48,6 +50,7 @@ class TiltWidget extends StatelessWidget { borderRadius: borderRadius, clipBehavior: clipBehavior, tiltConfig: tiltConfig, + lightShadowMode: lightShadowMode, lightConfig: lightConfig, shadowConfig: shadowConfig, onGestureMove: onGestureMove, From a7a5430f74bd1b0ef9f4856209142c3a68db7af4 Mon Sep 17 00:00:00 2001 From: Amos Date: Sat, 13 Apr 2024 18:13:24 +0800 Subject: [PATCH 3/5] Fix: borderRadius clipBehavior for child --- lib/src/widget/tilt_container.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/src/widget/tilt_container.dart b/lib/src/widget/tilt_container.dart index adcfc37..4ba5e14 100644 --- a/lib/src/widget/tilt_container.dart +++ b/lib/src/widget/tilt_container.dart @@ -217,7 +217,13 @@ class _TiltContainerState extends State with TiltTweenAnimation { shadowConfig: _shadowConfig, child: stackInner([ /// body - child ?? const SizedBox(), + Container( + decoration: BoxDecoration( + borderRadius: _borderRadius, + ), + clipBehavior: _clipBehavior, + child: child, + ), /// inner child ..._childLayout.inner, From 85076e22633eed89f54f121888632659b4269204 Mon Sep 17 00:00:00 2001 From: Amos Date: Fri, 6 Sep 2024 16:39:22 +0800 Subject: [PATCH 4/5] Fix: Test --- test/tilt_widget/tilt_gestures_drag_test.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/tilt_widget/tilt_gestures_drag_test.dart b/test/tilt_widget/tilt_gestures_drag_test.dart index 946ad7b..b2aae10 100644 --- a/test/tilt_widget/tilt_gestures_drag_test.dart +++ b/test/tilt_widget/tilt_gestures_drag_test.dart @@ -70,7 +70,7 @@ void main() { final Finder scrollFinder = find.byKey(const Key('scroll')); final ScrollController scrollController = ScrollController(); - /// 回调赋值 + debugDefaultTargetPlatformOverride = TargetPlatform.windows; await tester.pumpWidget( MaterialApp( home: Scaffold( @@ -95,6 +95,7 @@ void main() { ), ), ); + debugDefaultTargetPlatformOverride = null; /// onVerticalDragUpdate await tester.timedDrag( From b3a4beef203ccd86fcca05f38ca97132e8e4855a Mon Sep 17 00:00:00 2001 From: Amos Date: Tue, 10 Dec 2024 22:15:31 +0800 Subject: [PATCH 5/5] Feat: Tilt.lightShadowMode --- CHANGELOG.md | 4 +- README-ZH.md | 6 +- lib/src/widget/tilt_shadow.dart | 47 +++++++++++++-- pubspec.yaml | 2 +- .../tilt_widget/tilt_gestures_touch_test.dart | 59 +++++++++++++++++++ 5 files changed, 109 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c4d309..5877020 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,9 @@ > [!IMPORTANT] > See the [Migration Guide](guides/migration_guide.md) for the details of breaking changes between versions. -## 3.1.0 (Unreleased) +## 3.2.0 (Unreleased) + +## 3.2.0-rc.1 ### New features diff --git a/README-ZH.md b/README-ZH.md index 2bcd2f7..36a3355 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -230,7 +230,7 @@ Tilt( | 参数名 | 类型 | 默认值 | 描述 | | --- | --- | --- | --- | -| child `required` | `Widget` | - | 创建一个 widget,使 child widget 有倾斜效果。 | +| child `必选` | `Widget` | - | 创建一个 widget,使 child widget 有倾斜效果。 | | childLayout | [ChildLayout][] | `ChildLayout()` | 其它 child 布局。
例如:位于 child 外部、内部、后面的视差布局。 | | tiltStreamController | [StreamController<TiltStreamModel>][]? | `null` | 使用 `StreamController.broadcast()` 来自定义控制倾斜。 | | disable | `bool` | `false` | 禁用所有效果。 | @@ -250,7 +250,7 @@ Tilt( | 参数名 | 类型 | 默认值 | 描述 | | --- | --- | --- | --- | -| child `required` | `Widget` | - | 创建视差 widget。 | +| child `必选` | `Widget` | - | 创建视差 widget。 | | size | `Offset` | `Offset(10.0, 10.0)` | 视差大小。 | | filterQuality | `FilterQuality` | `null` | Flutter FilterQuality。 | @@ -290,7 +290,7 @@ tiltStreamController.add( | Parameter | Type | Default | Description | | --- | --- | --- | --- | -| position `required` | `Offset` | - | 当前触发的坐标位置,
会触发对应位置的倾斜效果。
例如:
有一个组件尺寸为 width: 10, height: 10,
(0, 0):会触发最左上的倾斜。
(10, 10):会触发最右下的倾斜。 | +| position `必选` | `Offset` | - | 当前触发的坐标位置,
会触发对应位置的倾斜效果。
例如:
有一个组件尺寸为 width: 10, height: 10,
(0, 0):会触发最左上的倾斜。
(10, 10):会触发最右下的倾斜。 | | gesturesType | `GesturesType` | `GesturesType.controller` | 触发手势类型。
会根据 [手势优先级](#手势优先级-) 进行触发。
如果需要自定义动画或其他方式自行控制,
`推荐` 使用 `GesturesType.controller`。
如果使用其他的类型进行触发,
那么就会受到对应类型相关配置、效果的影响。
例如:
自行触发 `GesturesType.sensors` 的时候。
配置 `TiltConfig.enableSensorRevert` 为 false 的情况下,
将同样不会复原至初始状态。 | | gestureUse | `bool` | `true` | 手势是否正在使用。
用于确定手势是否正在使用,并根据手势优先级进行处理。
例如:
如果在触发 `GesturesType.touch` 的时候永远不赋值为 false,那么优先级低于 `GesturesType.touch` 的手势将永远不会被触发。 | diff --git a/lib/src/widget/tilt_shadow.dart b/lib/src/widget/tilt_shadow.dart index 9d7a040..0753a90 100644 --- a/lib/src/widget/tilt_shadow.dart +++ b/lib/src/widget/tilt_shadow.dart @@ -1,4 +1,5 @@ import 'dart:ui'; +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/widgets.dart'; import '../config/tilt_light_config.dart'; @@ -210,11 +211,19 @@ class TiltShadowProjector extends TiltShadow { (shadowConfig.projectorBlurSigmaFrom - shadowConfig.projectorBlurSigmaTo); + Matrix4 get transform => Matrix4.zero() + ..setIdentity() + ..translate(offset.dx, offset.dy) + ..scale(scale, scale); + @override Widget build(BuildContext context) { - return IgnorePointer( - child: Transform.translate( - offset: offset, + if (shadowDisable) return const SizedBox(); + + /// TODO: BUG - 在 Web 端,Widget 嵌套顺序不同会导致渲染错误的奇怪效果,暂时单独处理。 + /// 目前测试下来是将 ImageFiltered 嵌套在 Transform 内引起的。 + if (kIsWeb) { + return IgnorePointer( child: ImageFiltered( imageFilter: ImageFilter.blur( sigmaX: blurSigma, @@ -228,7 +237,37 @@ class TiltShadowProjector extends TiltShadow { shadowConfig.color, BlendMode.srcATop, ), - child: Transform.scale(scale: scale, child: child), + child: Transform( + alignment: Alignment.center, + transform: transform, + child: child, + ), + ), + ), + ), + ); + } + + /// TODO: BUG - 在其他端,Widget 嵌套顺序不同会导致渲染错误的奇怪效果。 + /// 请暂时务必只保持以下嵌套顺序 + return IgnorePointer( + child: Transform( + alignment: Alignment.center, + transform: transform, + child: ColorFiltered( + colorFilter: ColorFilter.mode( + shadowConfig.color, + BlendMode.srcATop, + ), + child: ImageFiltered( + imageFilter: ImageFilter.blur( + sigmaX: blurSigma, + sigmaY: blurSigma, + tileMode: TileMode.decal, + ), + child: Opacity( + opacity: showShadow, + child: child, ), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index d371871..9dfc3e2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,7 @@ description: Easily apply tilt parallax hover effects for Flutter, which support # https://semver.org/spec/v2.0.0-rc.1.html # https://dart.dev/tools/pub/versioning#semantic-versions # https://dart.dev/tools/pub/dependencies#version-constraints -version: 3.1.0 +version: 3.2.0-rc.1 homepage: https://amoshuke.github.io/flutter_tilt_book repository: https://github.com/fluttercandies/flutter_tilt issue_tracker: https://github.com/fluttercandies/flutter_tilt/issues diff --git a/test/tilt_widget/tilt_gestures_touch_test.dart b/test/tilt_widget/tilt_gestures_touch_test.dart index 85a5f2f..2880217 100644 --- a/test/tilt_widget/tilt_gestures_touch_test.dart +++ b/test/tilt_widget/tilt_gestures_touch_test.dart @@ -527,5 +527,64 @@ void main() { expect(leaveGesturesTypeTest, GesturesType.touch); expect(leaveTiltDataTest, leaveTiltDataTestExpect); }); + + testWidgets('LightShadowMode.projector ShadowConfig.disable = true', + (WidgetTester tester) async { + TiltDataModel? moveTiltDataTest; + TiltDataModel? leaveTiltDataTest; + GesturesType? moveGesturesTypeTest; + GesturesType? leaveGesturesTypeTest; + final TiltDataModel leaveTiltDataTestExpect = TiltDataModel( + angle: Offset.zero, + areaProgress: Offset.zero, + position: const Offset(5.0, 5.0), + transform: Matrix4( + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.05, + 0.0, + 0.0, + 0.0, + 1.0, + ), + ); + + /// 回调赋值 + await tester.pumpWidget( + TiltWidget( + lightShadowMode: LightShadowMode.projector, + shadowConfig: const ShadowConfig(disable: true), + onGestureMove: (TiltDataModel tiltData, GesturesType gesturesType) { + moveTiltDataTest = tiltData; + moveGesturesTypeTest = gesturesType; + }, + onGestureLeave: (TiltDataModel tiltData, GesturesType gesturesType) { + leaveTiltDataTest = tiltData; + leaveGesturesTypeTest = gesturesType; + }, + ), + ); + + /// 倾斜 touch move + await tester.fling(tiltWidgetFinder, const Offset(0.0, 5.0), 0.1); + expect(childFinder, findsNWidgets(1)); + expect(moveGesturesTypeTest, GesturesType.touch); + expect(moveTiltDataTest != null, true); + + /// 倾斜 touch leave + await tester.fling(tiltScaffoldFinder, const Offset(0.0, -1.0), 0.1); + expect(childFinder, findsNWidgets(1)); + expect(leaveGesturesTypeTest, GesturesType.touch); + expect(leaveTiltDataTest, leaveTiltDataTestExpect); + }); }); }