refine: add milky translucency and curved reflections to Dock
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
const appVersion = '0.7.8';
|
||||
const appBuildNumber = 26;
|
||||
const appBuildNumber = 27;
|
||||
const appFilingNumber = '蜀ICP备2025169276号-5A';
|
||||
|
||||
@@ -142,9 +142,9 @@ class _GlassDockState extends State<GlassDock> with TickerProviderStateMixin {
|
||||
)!,
|
||||
]
|
||||
: [
|
||||
Colors.white.withValues(alpha: 0.05),
|
||||
_blue.withValues(alpha: 0.008),
|
||||
Colors.white.withValues(alpha: 0.018),
|
||||
Colors.white.withValues(alpha: 0.24),
|
||||
const Color(0xFFF9FBFF).withValues(alpha: 0.12),
|
||||
Colors.white.withValues(alpha: 0.18),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -154,8 +154,8 @@ class _GlassDockState extends State<GlassDock> with TickerProviderStateMixin {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
child: GlassRefraction(
|
||||
strength: lens ? 0.70 + pressure * 0.70 : 0.95,
|
||||
blurSigma: lens ? 1.6 - pressure * 0.6 : 1.4,
|
||||
strength: lens ? 0.78 + pressure * 0.70 : 1.05,
|
||||
blurSigma: lens ? 1.6 - pressure * 0.6 : 1.5,
|
||||
child: content,
|
||||
),
|
||||
);
|
||||
@@ -188,8 +188,8 @@ class _GlassDockState extends State<GlassDock> with TickerProviderStateMixin {
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(0xFF4C6078).withValues(alpha: 0.075),
|
||||
blurRadius: 16,
|
||||
offset: const Offset(0, 5),
|
||||
blurRadius: 18,
|
||||
offset: const Offset(0, 6),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -352,15 +352,15 @@ class _GlassDockState extends State<GlassDock> with TickerProviderStateMixin {
|
||||
);
|
||||
}
|
||||
|
||||
/// A diffuse silver bevel and thin directional glints show optical thickness.
|
||||
/// The painted highlights stay on the edge so background colour passes through.
|
||||
/// A white, rounded bevel catches light around a translucent milky body.
|
||||
/// Highlights stay at the edge; the backdrop remains visible beneath the glass.
|
||||
class _GlassRimPainter extends CustomPainter {
|
||||
const _GlassRimPainter({required this.lens, required this.pressure});
|
||||
final bool lens;
|
||||
final double pressure;
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final edge = lens ? const Color(0xFF7B838F) : const Color(0xFFAFBECF);
|
||||
final edge = lens ? const Color(0xFF7B838F) : const Color(0xFF9CAABD);
|
||||
// A quiet grey selection at rest; the glossy lens rim returns on touch.
|
||||
final opacity = lens ? 0.25 + pressure * 0.75 : 1.0;
|
||||
final bounds = Offset.zero & size;
|
||||
@@ -376,15 +376,15 @@ class _GlassRimPainter extends CustomPainter {
|
||||
outer,
|
||||
Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = lens ? 1.8 : 2.2
|
||||
..maskFilter = MaskFilter.blur(BlurStyle.normal, lens ? 0.9 : 0.45)
|
||||
..strokeWidth = lens ? 1.8 : 2.8
|
||||
..maskFilter = MaskFilter.blur(BlurStyle.normal, lens ? 0.9 : 0.65)
|
||||
..shader = ui.Gradient.linear(
|
||||
bounds.topLeft,
|
||||
bounds.bottomRight,
|
||||
[
|
||||
Colors.white.withValues(alpha: 0.24 * opacity),
|
||||
Colors.white.withValues(alpha: (lens ? 0.24 : 0.42) * opacity),
|
||||
edge.withValues(alpha: 0.04 * opacity),
|
||||
Colors.white.withValues(alpha: 0.14 * opacity),
|
||||
Colors.white.withValues(alpha: (lens ? 0.14 : 0.30) * opacity),
|
||||
],
|
||||
[0.0, 0.55, 1.0],
|
||||
),
|
||||
@@ -419,9 +419,9 @@ class _GlassRimPainter extends CustomPainter {
|
||||
bounds.topLeft,
|
||||
bounds.bottomRight,
|
||||
[
|
||||
Colors.white.withValues(alpha: 0.20 * opacity),
|
||||
Colors.white.withValues(alpha: (lens ? 0.20 : 0.34) * opacity),
|
||||
edge.withValues(alpha: (lens ? 0.13 : 0.07) * opacity),
|
||||
Colors.white.withValues(alpha: 0.28 * opacity),
|
||||
Colors.white.withValues(alpha: (lens ? 0.28 : 0.40) * opacity),
|
||||
],
|
||||
[0.0, 0.5, 1.0],
|
||||
),
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
name: hepan_everyday
|
||||
description: TPSchedule 学生课表
|
||||
publish_to: none
|
||||
version: 0.7.8+26
|
||||
version: 0.7.8+27
|
||||
environment:
|
||||
sdk: ^3.13.5
|
||||
dependencies:
|
||||
|
||||
@@ -29,7 +29,7 @@ void main() {
|
||||
float rim = 1.0 - smoothstep(0.0, radius * 0.46, depth);
|
||||
// Curved glass bends rays most at its edge, with subtle centre magnification.
|
||||
vec2 ray = centre + local / (1.0 + u_strength * 0.075)
|
||||
- normal * rim * rim * radius * u_strength * 0.20;
|
||||
- normal * rim * rim * radius * u_strength * 0.23;
|
||||
vec4 colour = texture(u_background, sampleUV(ray));
|
||||
float dispersion = u_strength * rim * 0.65;
|
||||
colour.r = texture(u_background, sampleUV(ray + normal * dispersion)).r;
|
||||
@@ -41,6 +41,9 @@ void main() {
|
||||
vec4 reflected = texture(u_background,
|
||||
sampleUV(point + normal * radius * 0.40));
|
||||
colour = mix(colour, reflected, min(edge_light * 0.18, 0.22));
|
||||
// A grazing-angle white reflection gives the curved rim visible thickness;
|
||||
// the broad centre continues to transmit the live, lightly softened scene.
|
||||
colour.rgb = mix(colour.rgb, vec3(colour.a), min(edge_light * 0.12, 0.16));
|
||||
float reflection = edge_light * 0.07;
|
||||
colour.rgb = min(colour.rgb + reflection * colour.a, vec3(colour.a));
|
||||
frag_color = colour;
|
||||
|
||||
@@ -9,6 +9,25 @@ import 'package:hepan_everyday/widgets/glass_dock.dart';
|
||||
import 'package:hepan_everyday/widgets/glass_refraction.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'Dock has a milky white body while transmitting a dark backdrop',
|
||||
(tester) async {
|
||||
final pixels = await _render(
|
||||
tester,
|
||||
GlassDock(selectedIndex: 0, onSelected: (_) {}),
|
||||
backdropColor: const Color(0xFF162536),
|
||||
preview: true,
|
||||
previewName: 'liquid-glass-milky-dark.png',
|
||||
);
|
||||
// Away from icons and the bevel: white glass lifts the dark background,
|
||||
// while its original cool colour remains visible through the material.
|
||||
final red = _channel(pixels, 160, 43, 0);
|
||||
final green = _channel(pixels, 160, 43, 1);
|
||||
expect(red, greaterThan(45));
|
||||
expect(red, lessThan(90));
|
||||
expect(green - red, closeTo(13, 6));
|
||||
},
|
||||
);
|
||||
testWidgets('resting cursor is neutral grey and clears while held', (
|
||||
tester,
|
||||
) async {
|
||||
@@ -21,6 +40,8 @@ void main() {
|
||||
// Sample beside the visible label, away from icon glow and glass rims.
|
||||
final selected = _channel(resting, 16, 43, 0);
|
||||
final base = _channel(resting, 160, 43, 0);
|
||||
// On a white page the white glass must not turn into a grey veil.
|
||||
expect(base, greaterThanOrEqualTo(240));
|
||||
expect(base - selected, greaterThan(20));
|
||||
expect((_channel(resting, 16, 43, 2) - selected).abs(), lessThan(12));
|
||||
final held = await _render(
|
||||
@@ -64,11 +85,11 @@ void main() {
|
||||
final red = _channel(pixels, 16, 40, 0);
|
||||
final green = _channel(pixels, 16, 40, 1);
|
||||
final blue = _channel(pixels, 16, 40, 2);
|
||||
expect(red, lessThan(85));
|
||||
expect(green - red, greaterThan(95));
|
||||
// The strongest resting tint (34%) and base veil (5%) retain about 63%
|
||||
// of the source's 210-level blue/red contrast, including the shorter rim.
|
||||
expect(blue - red, greaterThan(130));
|
||||
expect(red, lessThan(115));
|
||||
expect(green - red, greaterThanOrEqualTo(80));
|
||||
// The milky base and resting grey lens still transmit over half of the
|
||||
// cyan source's 210-level blue/red contrast rather than becoming opaque.
|
||||
expect(blue - red, greaterThan(105));
|
||||
},
|
||||
);
|
||||
|
||||
@@ -166,6 +187,7 @@ Future<ByteData> _render(
|
||||
double stripeWidth = 2,
|
||||
bool gradient = false,
|
||||
bool preview = false,
|
||||
String? previewName,
|
||||
Offset offset = Offset.zero,
|
||||
Color backdropColor = const Color(0xFF14B4E6),
|
||||
bool held = false,
|
||||
@@ -246,11 +268,13 @@ Future<ByteData> _render(
|
||||
final png = (await previewImage.toByteData(
|
||||
format: ui.ImageByteFormat.png,
|
||||
))!;
|
||||
final name = backdropColor == Colors.white
|
||||
? held
|
||||
? 'liquid-glass-held.png'
|
||||
: 'liquid-glass-rest.png'
|
||||
: 'liquid-glass-detail.png';
|
||||
final name =
|
||||
previewName ??
|
||||
(backdropColor == Colors.white
|
||||
? held
|
||||
? 'liquid-glass-held.png'
|
||||
: 'liquid-glass-rest.png'
|
||||
: 'liquid-glass-detail.png');
|
||||
final output = File('../dist/screenshots/$name');
|
||||
await output.parent.create(recursive: true);
|
||||
await output.writeAsBytes(png.buffer.asUint8List());
|
||||
|
||||
Reference in New Issue
Block a user