diff --git a/CHANGELOG.md b/CHANGELOG.md index aef8e1d..e0008a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ > [!IMPORTANT] > See the [Migration Guides](https://amoshuke.github.io/flutter_tilt_book/en/v4/docs/migration-guides/) for the details of breaking changes between versions. +## 4.0.2 (2026-06-05) + +**Fixes** + +- Fix light/shadow not reaching `maxIntensity` for the `LightDirection.around` and `LightDirection.all` directions. ([#42](https://github.com/fluttercandies/flutter_tilt/pull/42)) + Now that it is fixed, if the effect looks too strong, set `maxIntensity` to the square of its previous value (e.g. `0.5` → `0.25`, `0.6` → `0.36`). + ## 4.0.1 (2026-06-04) **Improvements** diff --git a/lib/src/config/tilt_light_config.dart b/lib/src/config/tilt_light_config.dart index e45ae6e..61bcb68 100644 --- a/lib/src/config/tilt_light_config.dart +++ b/lib/src/config/tilt_light_config.dart @@ -18,7 +18,7 @@ class LightConfig { this.disable = false, this.color = const Color(0xFFFFFFFF), this.minIntensity = 0.0, - this.maxIntensity = 0.5, + this.maxIntensity = 0.25, this.spreadFactor = 4.0, this.direction = LightDirection.around, this.enableReverse, diff --git a/lib/src/config/tilt_shadow_config.dart b/lib/src/config/tilt_shadow_config.dart index 95379fb..1b0effc 100644 --- a/lib/src/config/tilt_shadow_config.dart +++ b/lib/src/config/tilt_shadow_config.dart @@ -23,7 +23,7 @@ abstract class ShadowConfig { this.disable = false, this.color = const Color(0xFF9E9E9E), this.minIntensity = 0.0, - this.maxIntensity = 0.5, + this.maxIntensity = 0.25, this.offsetInitial = Offset.zero, this.offsetFactor = 0.1, this.direction, diff --git a/lib/src/internal/mixin/tilt_decoration_mixin.dart b/lib/src/internal/mixin/tilt_decoration_mixin.dart index 097a3bb..76af1e6 100644 --- a/lib/src/internal/mixin/tilt_decoration_mixin.dart +++ b/lib/src/internal/mixin/tilt_decoration_mixin.dart @@ -4,7 +4,7 @@ import '../../enums.dart'; import '../../utils/utils.dart'; mixin TiltDecorationMixin { - /// 计算提供的方向进度 + /// 计算提供的方向强度 /// /// 范围:0-1 /// @@ -14,17 +14,15 @@ mixin TiltDecorationMixin { /// - [ShadowDirection] 阴影方向 /// /// 可选项 - /// - [min] 最小进度限制 0-1 - /// - [max] 最大进度限制 0-1 - /// - [enableReverse] 开启反向 + /// - [min] 最小强度限制 0-1 + /// - [max] 最大强度限制 0-1 /// - /// @return [double] 方向进度 0-1 - double tiltDecorationDirectionProgress( + /// @return [double] 方向强度,映射到 [min] - [max] 区间 + double tiltDecorationDirectionIntensity( Offset areaProgress, Direction direction, { double min = 0.0, double max = 1.0, - bool enableReverse = false, }) { assert(min <= max && min >= 0.0 && max <= 1.0); @@ -32,151 +30,122 @@ mixin TiltDecorationMixin { final progress = -areaProgress; final progressX = progress.dx, progressY = progress.dy; - /// 根据方向计算进度 - var progressData = _calculateDirectionProgress( + /// 根据方向计算强度 + var intensity = _calculateDirectionIntensity( direction, progressX, progressY, - min, - max, ); - /// 强度调整 - progressData *= max; + /// 将强度限制在 0-1 + intensity = intensity.clamp(0.0, 1.0); - /// 反向处理 - if (enableReverse) progressData = -progressData; + /// 按 min-max 区间映射:强度 0 对应 min,强度 1 对应 max + intensity = min + intensity * (max - min); - /// 避免超出范围 - progressData = progressData.clamp(min, max); - - return progressData; + return intensity; } - /// 根据方向计算进度 + /// 根据方向计算强度 /// /// - [direction] 光照方向 /// - [progressX] 当前水平进度 /// - [progressY] 当前垂直进度 - /// - [min] 最小进度限制 0-1 - /// - [max] 最大进度限制 0-1 /// - /// @return 对应方向的进度 - double _calculateDirectionProgress( + /// @return 对应方向的强度 + double _calculateDirectionIntensity( Direction direction, double progressX, double progressY, - double min, - double max, ) { return switch (direction) { - LightDirection _ => _calculateLightDirectionProgress( + LightDirection _ => _calculateLightDirectionIntensity( direction, progressX, progressY, - min, - max, ), - ShadowDirection _ => _calculateShadowDirectionProgress( + ShadowDirection _ => _calculateShadowDirectionIntensity( direction, progressX, progressY, - min, - max, ), }; } - /// 计算距离中心点的距离,并限制在最小和最大范围内 + /// 计算距离中心点的距离,并限制在 0-1 范围内 /// /// - [progressX] 当前水平进度 /// - [progressY] 当前垂直进度 - /// - [min] 最小进度限制 0-1 - /// - [max] 最大进度限制 0-1 /// - /// @return 对应方向的进度 + /// @return 限制在 0-1 范围内的距离 double _constrainedDistance( double progressX, double progressY, - double min, - double max, ) { final distance = Utils.p2pDistance( Offset.zero, Offset(progressX, progressY), ); - return distance.clamp(min, max); + return distance.clamp(0, 1); } - /// 计算光照方向的进度 + /// 计算光照方向的强度 /// /// - [direction] 光照方向 /// - [progressX] 当前水平进度 /// - [progressY] 当前垂直进度 - /// - [min] 最小进度限制 0-1 - /// - [max] 最大进度限制 0-1 /// - /// @return 对应方向的进度 - double _calculateLightDirectionProgress( + /// @return 对应方向的强度 + double _calculateLightDirectionIntensity( LightDirection direction, double progressX, double progressY, - double min, - double max, ) { return switch (direction) { LightDirection.none => 0.0, - LightDirection.around => - _constrainedDistance(progressX, progressY, min, max), - LightDirection.all => max, + LightDirection.around => _constrainedDistance(progressX, progressY), + LightDirection.all => 1, LightDirection.top => progressY, LightDirection.bottom => -progressY, LightDirection.left => progressX, LightDirection.right => -progressX, - LightDirection.center => - max - _constrainedDistance(progressX, progressY, min, max), + LightDirection.center => 1 - _constrainedDistance(progressX, progressY), LightDirection.topLeft => progressX + progressY, LightDirection.bottomRight => -(progressX + progressY), LightDirection.topRight => -(progressX - progressY), LightDirection.bottomLeft => progressX - progressY, - LightDirection.xCenter => max - progressY.abs(), - LightDirection.yCenter => max - progressX.abs(), + LightDirection.xCenter => 1 - progressY.abs(), + LightDirection.yCenter => 1 - progressX.abs(), }; } - /// 计算阴影方向的进度 + /// 计算阴影方向的强度 /// /// - [direction] 阴影方向 /// - [progressX] 当前水平进度 /// - [progressY] 当前垂直进度 - /// - [min] 最小进度限制 0-1 - /// - [max] 最大进度限制 0-1 /// - /// @return 对应方向的进度 - double _calculateShadowDirectionProgress( + /// @return 对应方向的强度 + double _calculateShadowDirectionIntensity( ShadowDirection direction, double progressX, double progressY, - double min, - double max, ) { return switch (direction) { ShadowDirection.none => 0.0, - ShadowDirection.around => - _constrainedDistance(progressX, progressY, min, max), - ShadowDirection.all => max, + ShadowDirection.around => _constrainedDistance(progressX, progressY), + ShadowDirection.all => 1, ShadowDirection.top => -progressY, ShadowDirection.bottom => progressY, ShadowDirection.left => -progressX, ShadowDirection.right => progressX, - ShadowDirection.center => - max - _constrainedDistance(progressX, progressY, min, max), + ShadowDirection.center => 1 - _constrainedDistance(progressX, progressY), ShadowDirection.topLeft => -(progressX + progressY), ShadowDirection.bottomRight => progressX + progressY, ShadowDirection.topRight => progressX - progressY, ShadowDirection.bottomLeft => -(progressX - progressY), - ShadowDirection.xCenter => max - progressY.abs(), - ShadowDirection.yCenter => max - progressX.abs(), + ShadowDirection.xCenter => 1 - progressY.abs(), + ShadowDirection.yCenter => 1 - progressX.abs(), }; } } diff --git a/lib/src/widgets/effects/tilt_light.dart b/lib/src/widgets/effects/tilt_light.dart index d0f1f8a..abddfe7 100644 --- a/lib/src/widgets/effects/tilt_light.dart +++ b/lib/src/widgets/effects/tilt_light.dart @@ -69,8 +69,8 @@ class TiltLight extends StatelessWidget with TiltDecorationMixin { /// 定位 y (从中心位置开始) final positionY = p2cPosition.dy; - /// 光源方向进度 - final showProgress = tiltDecorationDirectionProgress( + /// 光源方向强度 + final intensity = tiltDecorationDirectionIntensity( areaProgress, lightConfig.direction, min: lightConfig.minIntensity, @@ -78,7 +78,7 @@ class TiltLight extends StatelessWidget with TiltDecorationMixin { ); /// 光源颜色的 Alpha 进度 - final alphaProgress = 255.0 * showProgress; + final alphaProgress = 255.0 * intensity; return Positioned( left: enableReverse ? positionX : null, diff --git a/lib/src/widgets/effects/tilt_shadow.dart b/lib/src/widgets/effects/tilt_shadow.dart index 2a51a5d..97f29d3 100644 --- a/lib/src/widgets/effects/tilt_shadow.dart +++ b/lib/src/widgets/effects/tilt_shadow.dart @@ -41,7 +41,7 @@ abstract class TiltShadow double get showShadow { final direction = (shadowConfig.direction ?? lightConfig.direction) as Direction; - return tiltDecorationDirectionProgress( + return tiltDecorationDirectionIntensity( areaProgress, direction, min: shadowConfig.minIntensity, diff --git a/pubspec.yaml b/pubspec.yaml index 7a743a2..1838ef4 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.html # https://dart.dev/tools/pub/versioning#semantic-versions # https://dart.dev/tools/pub/dependencies#version-constraints -version: 4.0.1 +version: 4.0.2 homepage: https://amoshuke.github.io/flutter_tilt_book documentation: https://amoshuke.github.io/flutter_tilt_book repository: https://github.com/fluttercandies/flutter_tilt diff --git a/test/internal/mixin/tilt_decoration_mixin_test.dart b/test/internal/mixin/tilt_decoration_mixin_test.dart index e4eae92..1f628b7 100644 --- a/test/internal/mixin/tilt_decoration_mixin_test.dart +++ b/test/internal/mixin/tilt_decoration_mixin_test.dart @@ -5,12 +5,11 @@ import 'package:flutter_tilt/src/internal/mixin/tilt_decoration_mixin.dart'; void main() { group('TiltDecorationMixin ::', () { final tiltDecorationTest = TiltDecorationTest(); - final tiltDecorationDirectionProgress = - tiltDecorationTest.tiltDecorationDirectionProgress; - test('tiltDecorationDirectionProgress', () { - /// enableReverse = false + final tiltDecorationDirectionIntensity = + tiltDecorationTest.tiltDecorationDirectionIntensity; + test('tiltDecorationDirectionIntensity', () { expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(0.0, 1.0), LightDirection.bottom, ), @@ -18,7 +17,7 @@ void main() { reason: '合法的 LightDirection', ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(0.0, -1.0), LightDirection.bottom, ), @@ -26,7 +25,7 @@ void main() { reason: '不合法的 LightDirection', ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(0.0, -1.0), ShadowDirection.bottom, ), @@ -34,406 +33,481 @@ void main() { reason: '合法的 ShadowDirection', ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(0.0, 1.0), ShadowDirection.bottom, ), 0.0, reason: '不合法的 ShadowDirection', ); - - /// enableReverse = true - expect( - tiltDecorationDirectionProgress( - const Offset(0.0, -1.0), - LightDirection.bottom, - enableReverse: true, - ), - 1.0, - reason: '合法的 LightDirection', - ); - expect( - tiltDecorationDirectionProgress( - const Offset(0.0, 1.0), - LightDirection.bottom, - enableReverse: true, - ), - 0.0, - reason: '不合法的 LightDirection', - ); - expect( - tiltDecorationDirectionProgress( - const Offset(0.0, 1.0), - ShadowDirection.bottom, - enableReverse: true, - ), - 1.0, - reason: '合法的 ShadowDirection', - ); - expect( - tiltDecorationDirectionProgress( - const Offset(0.0, -1.0), - ShadowDirection.bottom, - enableReverse: true, - ), - 0.0, - reason: '不合法的 ShadowDirection', - ); }); - test('tiltDecorationDirectionProgress LightDirection', () { + test('tiltDecorationDirectionIntensity LightDirection', () { expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), LightDirection.none, ), 0.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), LightDirection.all, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( Offset.zero, LightDirection.all, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(-1.0, -1.0), LightDirection.all, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), LightDirection.around, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( Offset.zero, LightDirection.around, ), 0.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(-1.0, -1.0), LightDirection.around, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(0.0, -1.0), LightDirection.top, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(0.0, 1.0), LightDirection.bottom, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(-1.0, 0.0), LightDirection.left, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 0.0), LightDirection.right, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( Offset.zero, LightDirection.center, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), LightDirection.center, ), 0.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(-1.0, -1.0), LightDirection.topLeft, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, -1.0), LightDirection.topRight, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(-1.0, 1.0), LightDirection.bottomLeft, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), LightDirection.bottomRight, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(-1.0, 0.0), LightDirection.xCenter, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( Offset.zero, LightDirection.xCenter, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 0.0), LightDirection.xCenter, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), LightDirection.xCenter, ), 0.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(0.0, -1.0), LightDirection.yCenter, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( Offset.zero, LightDirection.yCenter, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(0.0, 1.0), LightDirection.yCenter, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), LightDirection.yCenter, ), 0.0, ); }); - test('tiltDecorationDirectionProgress ShadowDirection', () { + test('tiltDecorationDirectionIntensity ShadowDirection', () { expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), ShadowDirection.none, ), 0.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), ShadowDirection.all, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( Offset.zero, ShadowDirection.all, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(-1.0, -1.0), ShadowDirection.all, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), ShadowDirection.around, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( Offset.zero, ShadowDirection.around, ), 0.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(-1.0, -1.0), ShadowDirection.around, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(0.0, 1.0), ShadowDirection.top, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(0.0, -1.0), ShadowDirection.bottom, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 0.0), ShadowDirection.left, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(-1.0, 0.0), ShadowDirection.right, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( Offset.zero, ShadowDirection.center, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), ShadowDirection.center, ), 0.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), ShadowDirection.topLeft, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(-1.0, 1.0), ShadowDirection.topRight, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, -1.0), ShadowDirection.bottomLeft, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(-1.0, -1.0), ShadowDirection.bottomRight, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(-1.0, 0.0), ShadowDirection.xCenter, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( Offset.zero, ShadowDirection.xCenter, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 0.0), ShadowDirection.xCenter, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), ShadowDirection.xCenter, ), 0.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(0.0, -1.0), ShadowDirection.yCenter, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( Offset.zero, ShadowDirection.yCenter, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(0.0, 1.0), ShadowDirection.yCenter, ), 1.0, ); expect( - tiltDecorationDirectionProgress( + tiltDecorationDirectionIntensity( const Offset(1.0, 1.0), ShadowDirection.yCenter, ), 0.0, ); }); + test('tiltDecorationDirectionIntensity min-max mapping', () { + /// 强度 0 → min,强度 1 → max,中间线性映射 + + /// 强度 1(满)→ max + expect( + tiltDecorationDirectionIntensity( + const Offset(1.0, 1.0), + LightDirection.all, + min: 0.2, + max: 0.8, + ), + 0.8, + ); + + /// 强度 0(无)→ min + expect( + tiltDecorationDirectionIntensity( + const Offset(1.0, 1.0), + LightDirection.none, + min: 0.2, + max: 0.8, + ), + 0.2, + ); + + /// 负向强度被截断到 0 → min + expect( + tiltDecorationDirectionIntensity( + const Offset(0.0, -1.0), + LightDirection.bottom, + min: 0.2, + max: 0.8, + ), + 0.2, + ); + + /// 正向满强度 → max + expect( + tiltDecorationDirectionIntensity( + const Offset(0.0, 1.0), + LightDirection.bottom, + min: 0.2, + max: 0.8, + ), + 0.8, + ); + + /// 中间强度 0.5 → min + 0.5 * (max - min) + expect( + tiltDecorationDirectionIntensity( + const Offset(0.0, 0.5), + LightDirection.bottom, + min: 0.2, + max: 0.8, + ), + closeTo(0.5, 1e-10), + ); + + /// 中心方向:正中心强度 1 → max + expect( + tiltDecorationDirectionIntensity( + Offset.zero, + LightDirection.center, + min: 0.2, + max: 0.8, + ), + 0.8, + ); + + /// around 方向:正中心强度 0 → min + expect( + tiltDecorationDirectionIntensity( + Offset.zero, + LightDirection.around, + min: 0.2, + max: 0.8, + ), + 0.2, + ); + + /// around 方向:距离超出对角线被截断到 1 → max + expect( + tiltDecorationDirectionIntensity( + const Offset(1.0, 1.0), + LightDirection.around, + min: 0.2, + max: 0.8, + ), + 0.8, + ); + + /// around 方向:中间距离 0.5 → min + 0.5 * (max - min) + expect( + tiltDecorationDirectionIntensity( + const Offset(0.0, 0.5), + LightDirection.around, + min: 0.2, + max: 0.8, + ), + closeTo(0.5, 1e-10), + ); + + /// 阴影方向和光照方向的 min-max 映射一致,只是强度计算是反向的 + expect( + tiltDecorationDirectionIntensity( + const Offset(0.0, -1.0), + ShadowDirection.bottom, + min: 0.3, + max: 0.9, + ), + closeTo(0.9, 1e-10), + ); + }); }); }