Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/ConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public static class ConfigManager
public static double EffectOpacity { get; set; } = 1.0;
public static double EffectSpeed { get; set; } = 1.0;
public static bool UseLinkedAnimationSpeed { get; set; } = true;
public static bool ApplyCurveDraw { get; set; } = false;
public static double TrailAnimationSpeed { get; set; } = 1.0;
public static double ClickAnimationSpeed { get; set; } = 1.0;
public static int TrailRefreshRate { get; set; } = 40;
Expand Down Expand Up @@ -130,6 +131,7 @@ public static void Load()
EffectOpacity = Math.Clamp(Convert.ToDouble(key.GetValue("EffectOpacity", 1.0)), 0.1, 1.0);
EffectSpeed = Math.Clamp(Convert.ToDouble(key.GetValue("EffectSpeed", 1.0)), 0.2, 3.0);
UseLinkedAnimationSpeed = Convert.ToBoolean(key.GetValue("UseLinkedAnimationSpeed", true));
ApplyCurveDraw = Convert.ToBoolean(key.GetValue("ApplyCurveDraw", false));
TrailAnimationSpeed = Math.Clamp(Convert.ToDouble(key.GetValue("TrailAnimationSpeed", EffectSpeed)), 0.2, 3.0);
ClickAnimationSpeed = Math.Clamp(Convert.ToDouble(key.GetValue("ClickAnimationSpeed", EffectSpeed)), 0.2, 3.0);
TrailRefreshRate = Math.Clamp(Convert.ToInt32(key.GetValue("TrailRefreshRate", 40)), 10, 240);
Expand Down Expand Up @@ -532,6 +534,7 @@ public static void ResetAndClear()
EffectOpacity = 1.0;
EffectSpeed = 1.0;
UseLinkedAnimationSpeed = true;
ApplyCurveDraw = false;
TrailAnimationSpeed = 1.0;
ClickAnimationSpeed = 1.0;
TrailRefreshRate = 40;
Expand Down
4 changes: 4 additions & 0 deletions src/ControlPanelWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,10 @@
Checked="LinkedAnimationSpeed_Changed" Unchecked="LinkedAnimationSpeed_Changed"/>
<TextBlock x:Name="TxtLinkedSpeedHint" Text="关闭此项后可单独加快或减慢拖尾衰减与移动粒子,而不影响点击波纹与爆发粒子,与下方「拖尾刷新率」不同。" FontSize="11" Foreground="#999" Margin="0,0,0,10" TextWrapping="Wrap"/>

<CheckBox Name="CheckApplyCurveDraw" Content="采用曲线拖尾" Style="{StaticResource ToggleSwitch}" Margin="0,0,0,4"
Checked="CurveDraw_Changed" Unchecked="CurveDraw_Changed"/>
<TextBlock x:Name="TxtCurveDrawHint" Text="建议在60Hz及以上刷新率开启" FontSize="11" Foreground="#999" Margin="0,0,0,10" TextWrapping="Wrap"/>

<StackPanel Name="PanelUnifiedAnimationSpeed" Margin="0,0,0,15">
<DockPanel LastChildFill="False">
<TextBlock x:Name="TxtAnimSpeed" Text="动画播放速度" Foreground="#555" FontSize="13" VerticalAlignment="Center"/>
Expand Down
9 changes: 9 additions & 0 deletions src/ControlPanelWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@
SliderScale.Value = ConfigManager.EffectScale;
SliderOpacity.Value = ConfigManager.EffectOpacity * 100;
CheckLinkedAnimationSpeed.IsChecked = ConfigManager.UseLinkedAnimationSpeed;
CheckApplyCurveDraw.IsChecked = ConfigManager.ApplyCurveDraw;
SliderSpeed.Value = ConfigManager.EffectSpeed;
SliderTrailAnimSpeed.Value = ConfigManager.TrailAnimationSpeed;
SliderClickAnimSpeed.Value = ConfigManager.ClickAnimationSpeed;
Expand Down Expand Up @@ -1317,6 +1318,7 @@
App.Overlay?.UpdateColor(ConfigManager.ParticleColor);
App.Overlay?.UpdateEffectSettings(effectScale, effectOpacity, trailSp, clickSp);
App.Overlay?.UpdateTrailRefreshRate(trailRefreshRate);
App.Overlay?.SetCurveDraw(CheckApplyCurveDraw.IsChecked ?? false);

VisualResetOverlay.Visibility = Visibility.Collapsed;
System.Windows.MessageBox.Show(
Expand Down Expand Up @@ -1423,6 +1425,7 @@
ConfigManager.Save("ClickTriggerType", clickType);
ConfigManager.Save("EnableMiddleClickTrigger", middleClickEnabled);
ConfigManager.Save("ScreenshotCompatibilityMode", screenshotCompatibilityEnabled);
ConfigManager.Save("ApplyCurveDraw", CheckApplyCurveDraw.IsChecked ?? false);

string sidebarBackgroundPath = TxtSidebarBackgroundPath?.Text?.Trim() ?? string.Empty;
if (!string.IsNullOrWhiteSpace(sidebarBackgroundPath))
Expand Down Expand Up @@ -1484,6 +1487,7 @@
App.Overlay?.RefreshEnvironmentFilterState();
App.Overlay?.UpdateTouchMode(isTouchscreenEnabled);
App.Overlay?.UpdateScreenshotCompatibilityMode(screenshotCompatibilityEnabled);
App.Overlay?.SetCurveDraw(CheckApplyCurveDraw.IsChecked ?? false);
if (!previousEnabledScreenIds.SetEquals(selectedIds))
{
App.Overlay?.RefreshScreenSelection();
Expand Down Expand Up @@ -1586,7 +1590,7 @@
string? exePath = AutoStartManager.ResolveExecutablePath(
Environment.ProcessPath,
Process.GetCurrentProcess().MainModule?.FileName,
Assembly.GetExecutingAssembly().Location,

Check warning on line 1593 in src/ControlPanelWindow.xaml.cs

View workflow job for this annotation

GitHub Actions / build

'System.Reflection.Assembly.Location' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'.
AppDomain.CurrentDomain.BaseDirectory);

if (string.IsNullOrEmpty(exePath)) return;
Expand Down Expand Up @@ -1694,6 +1698,11 @@
if (!IsLoaded) return;
}

private void CurveDraw_Changed(object sender, RoutedEventArgs e)
{
if (!IsLoaded) return;
}

private void LinkedAnimationSpeed_Changed(object sender, RoutedEventArgs e)
{
if (!IsLoaded || _suspendLinkedAnimationUiHandlers)
Expand Down
5 changes: 5 additions & 0 deletions src/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ public void UpdateTrailRefreshRate(int hz)
_ = hz;
}

public void SetCurveDraw(bool enabled)
{
ExecuteScript($"window.ApplyCurveDraw = {(enabled ? "true" : "false")};");
}

public void UpdateTouchMode(bool enabled)
{
ConfigManager.IsTouchscreenMode = enabled;
Expand Down
1 change: 1 addition & 0 deletions src/OverlayManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ public void UpdateScreenshotCompatibilityMode(bool enabled)
ForEachOverlay(w => w.UpdateScreenshotCompatibilityMode(enabled));
SyncScreenshotCompatCaptureSurfaces();
}
public void SetCurveDraw(bool enabled) => ForEachOverlay(w => w.SetCurveDraw(enabled));
public bool IsEffectSuppressedByEnvironment() => _isSuppressedByEnvironment;
public void RefreshEnvironmentFilterState()
{
Expand Down
6 changes: 6 additions & 0 deletions src/Resources/Strings.en.resx
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,12 @@ Download now?</value>
<data name="Visual_LinkedSpeedHint" xml:space="preserve">
<value>When off, trail and click speeds are independent of trail refresh rate below.</value>
</data>
<data name="Visual_CurveDraw" xml:space="preserve">
<value>Use curved trail lines</value>
</data>
<data name="Visual_CurveDrawHint" xml:space="preserve">
<value>Recommended for 60Hz and above</value>
</data>
<data name="Visual_Opacity" xml:space="preserve">
<value>Global opacity</value>
</data>
Expand Down
6 changes: 6 additions & 0 deletions src/Resources/Strings.ja.resx
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,12 @@
<data name="Visual_LinkedSpeedHint" xml:space="preserve">
<value>オフにするとトレイルとクリックの速度を個別に設定できます(下の更新率とは別)。</value>
</data>
<data name="Visual_CurveDraw" xml:space="preserve">
<value>曲線トレイルを使用</value>
</data>
<data name="Visual_CurveDrawHint" xml:space="preserve">
<value>60Hz以上のリフレッシュレートでの使用を推奨します</value>
</data>
<data name="Visual_Opacity" xml:space="preserve">
<value>全体の不透明度</value>
</data>
Expand Down
6 changes: 6 additions & 0 deletions src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,12 @@
<data name="Visual_LinkedSpeedHint" xml:space="preserve">
<value>关闭此项后可单独加快或减慢拖尾衰减与移动粒子,而不影响点击波纹与爆发粒子,与下方「拖尾刷新率」不同。</value>
</data>
<data name="Visual_CurveDraw" xml:space="preserve">
<value>采用曲线拖尾</value>
</data>
<data name="Visual_CurveDrawHint" xml:space="preserve">
<value>建议在60Hz及以上刷新率开启</value>
</data>
<data name="Visual_Opacity" xml:space="preserve">
<value>全局不透明度</value>
</data>
Expand Down
2 changes: 2 additions & 0 deletions src/UiLocalizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ public static void ApplyControlPanel(ControlPanelWindow w)
w.TxtVisualOpacity.Text = Localization.Get("Visual_Opacity");
w.CheckLinkedAnimationSpeed.Content = Localization.Get("Visual_LinkedSpeed");
w.TxtLinkedSpeedHint.Text = Localization.Get("Visual_LinkedSpeedHint");
w.CheckApplyCurveDraw.Content = Localization.Get("Visual_CurveDraw");
w.TxtCurveDrawHint.Text = Localization.Get("Visual_CurveDrawHint");
w.TxtAnimSpeed.Text = Localization.Get("Visual_AnimSpeed");
w.TxtTrailAnimSpeed.Text = Localization.Get("Visual_TrailAnimSpeed");
w.TxtClickAnimSpeed.Text = Localization.Get("Visual_ClickAnimSpeed");
Expand Down
60 changes: 45 additions & 15 deletions src/Web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -337,23 +337,53 @@
ctx.shadowOffsetY = 0;

const lastIdx = pts.length - 1;
for (let i = 0; i < lastIdx; i++) {
const alphaStart = i / lastIdx;
const alphaEnd = (i + 1) / lastIdx;
const a0 = pts[i];
const a1 = pts[i + 1];
// 每段的透明度基于数组索引位置,而非空间位置
if (window.ApplyCurveDraw) {
// 逐段渲染(Catmull-Rom → Cubic Bezier 曲线版)
for (let i = 0; i < lastIdx; i++) {
const a0 = pts[i];
const a1 = pts[i + 1];

// Catmull-Rom 控制点:CP1 = P_i + (P_{i+1} - P_{i-1}) / 6
// CP2 = P_{i+1} - (P_{i+2} - P_i) / 6
const prev = i > 0 ? pts[i - 1] : a0;
const next = i < lastIdx - 1 ? pts[i + 2] : a1;
const cp1x = a0.x + (a1.x - prev.x) / 6;
const cp1y = a0.y + (a1.y - prev.y) / 6;
const cp2x = a1.x - (next.x - a0.x) / 6;
const cp2y = a1.y - (next.y - a0.y) / 6;

const alphaStart = i / lastIdx;
const alphaEnd = (i + 1) / lastIdx;
const segGrad = ctx.createLinearGradient(a0.x, a0.y, a1.x, a1.y);
segGrad.addColorStop(0, `rgba(${this.color}, ${alphaStart})`);
segGrad.addColorStop(1, `rgba(${this.color}, ${alphaEnd})`);

const segGrad = ctx.createLinearGradient(a0.x, a0.y, a1.x, a1.y);
segGrad.addColorStop(0, `rgba(${this.color}, ${alphaStart})`);
segGrad.addColorStop(1, `rgba(${this.color}, ${alphaEnd})`);

ctx.beginPath();
ctx.moveTo(a0.x, a0.y);
ctx.lineTo(a1.x, a1.y);
ctx.strokeStyle = segGrad;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(a0.x, a0.y);
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, a1.x, a1.y);
ctx.strokeStyle = segGrad;
ctx.stroke();
}
} else {
// 逐段渲染(直线版)
for (let i = 0; i < lastIdx; i++) {
const alphaStart = i / lastIdx;
const alphaEnd = (i + 1) / lastIdx;
const a0 = pts[i];
const a1 = pts[i + 1];

const segGrad = ctx.createLinearGradient(a0.x, a0.y, a1.x, a1.y);
segGrad.addColorStop(0, `rgba(${this.color}, ${alphaStart})`);
segGrad.addColorStop(1, `rgba(${this.color}, ${alphaEnd})`);

ctx.beginPath();
ctx.moveTo(a0.x, a0.y);
ctx.lineTo(a1.x, a1.y);
ctx.strokeStyle = segGrad;
ctx.stroke();
}
}

ctx.shadowColor = 'transparent';
}

Expand Down
Loading