feat: Refactor fear tracker styling to use CSS variables and enable advanced gradient customization.
This commit is contained in:
parent
4fd16bd9fb
commit
97ff59e703
3 changed files with 166 additions and 79 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "dh-feartrackerplus",
|
"id": "dh-feartrackerplus",
|
||||||
"title": "Daggerheart Fear Tracker Plus",
|
"title": "Daggerheart Fear Tracker Plus",
|
||||||
"version": "1.2.0",
|
"version": "1.3.0",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "13",
|
"minimum": "13",
|
||||||
"verified": "13"
|
"verified": "13"
|
||||||
|
|
@ -32,6 +32,6 @@
|
||||||
],
|
],
|
||||||
"url": "https://github.com/cptn-cosmo/dh-feartrackerplus",
|
"url": "https://github.com/cptn-cosmo/dh-feartrackerplus",
|
||||||
"manifest": "https://github.com/cptn-cosmo/dh-feartrackerplus/releases/latest/download/module.json",
|
"manifest": "https://github.com/cptn-cosmo/dh-feartrackerplus/releases/latest/download/module.json",
|
||||||
"download": "https://github.com/cptn-cosmo/dh-feartrackerplus/releases/download/1.2.0/dh-feartrackerplus.zip",
|
"download": "https://github.com/cptn-cosmo/dh-feartrackerplus/releases/download/1.3.0/dh-feartrackerplus.zip",
|
||||||
"description": "Customizes the Fear Tracker for Daggerheart."
|
"description": "Customizes the Fear Tracker for Daggerheart."
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +95,7 @@ Hooks.once('init', () => {
|
||||||
scope: 'client',
|
scope: 'client',
|
||||||
config: true,
|
config: true,
|
||||||
type: String,
|
type: String,
|
||||||
default: '#ff0000',
|
default: '#000000',
|
||||||
onChange: refreshFearTracker
|
onChange: refreshFearTracker
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -390,23 +390,31 @@ function injectFearCustomization(html) {
|
||||||
let themeEnd = null;
|
let themeEnd = null;
|
||||||
|
|
||||||
// Handle Themes
|
// Handle Themes
|
||||||
if (colorTheme !== 'custom' && colorTheme !== 'foundryborne') {
|
if (colorTheme !== 'custom') { // Removed check for 'foundryborne' to treat it as a preset
|
||||||
const themes = {
|
const themes = {
|
||||||
|
'foundryborne': { start: null, end: null, empty: 'transparent' },
|
||||||
'hope-fear': { start: '#FFC107', end: '#512DA8', empty: '#2e1c4a' },
|
'hope-fear': { start: '#FFC107', end: '#512DA8', empty: '#2e1c4a' },
|
||||||
'blood-moon': { start: '#5c0000', end: '#ff0000', empty: '#2a0000' },
|
'blood-moon': { start: '#5c0000', end: '#ff0000', empty: '#2a0000' },
|
||||||
'ethereal': { start: '#00FFFF', end: '#0000FF', empty: '#002a33' },
|
'ethereal': { start: '#00FFFF', end: '#0000FF', empty: '#002a33' },
|
||||||
'toxic': { start: '#00FF00', end: '#FFFF00', empty: '#003300' }
|
'toxic': { start: '#00FF00', end: '#FFFF00', empty: '#003300' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const theme = themes[colorTheme];
|
const theme = themes[colorTheme];
|
||||||
if (theme) {
|
if (theme) {
|
||||||
|
// Standard Interpolated Preset
|
||||||
themeStart = theme.start;
|
themeStart = theme.start;
|
||||||
themeEnd = theme.end;
|
themeEnd = theme.end;
|
||||||
emptyColor = theme.empty;
|
|
||||||
// Fallback fullColor for non-interpolation uses if any
|
|
||||||
fullColor = theme.start;
|
fullColor = theme.start;
|
||||||
|
|
||||||
|
emptyColor = theme.empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
emptyColor = theme.empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Apply Scaling
|
// Apply Scaling
|
||||||
const scale = game.settings.get(MODULE_ID, 'trackerScale');
|
const scale = game.settings.get(MODULE_ID, 'trackerScale');
|
||||||
if (scale !== 1.0) {
|
if (scale !== 1.0) {
|
||||||
|
|
@ -433,7 +441,9 @@ function injectFearCustomization(html) {
|
||||||
icons.forEach((icon, index) => {
|
icons.forEach((icon, index) => {
|
||||||
// 1. Reset Icon State
|
// 1. Reset Icon State
|
||||||
// Remove common FA prefixes just in case
|
// Remove common FA prefixes just in case
|
||||||
icon.classList.remove('fa-skull', 'fas', 'far', 'fal', 'fad', 'fab');
|
icon.classList.remove('fa-skull', 'fas', 'far', 'fal', 'fad', 'fab', 'dh-fear-plus-bg-override', 'dh-fear-plus-icon-override');
|
||||||
|
|
||||||
|
const isInactive = icon.classList.contains('inactive');
|
||||||
|
|
||||||
// Remove old SVG img if present from previous renders
|
// Remove old SVG img if present from previous renders
|
||||||
const oldImg = icon.querySelector('img.fear-tracker-icon');
|
const oldImg = icon.querySelector('img.fear-tracker-icon');
|
||||||
|
|
@ -460,90 +470,122 @@ function injectFearCustomization(html) {
|
||||||
const newClasses = iconClass.split(' ').filter(c => c.trim() !== '');
|
const newClasses = iconClass.split(' ').filter(c => c.trim() !== '');
|
||||||
icon.classList.add(...newClasses, 'fear-tracker-plus-custom');
|
icon.classList.add(...newClasses, 'fear-tracker-plus-custom');
|
||||||
|
|
||||||
// Icon Color Application
|
|
||||||
if (iconColor && iconColor !== '#ffffff') {
|
|
||||||
// Check if it's a gradient
|
|
||||||
if (iconColor.includes('gradient')) {
|
|
||||||
icon.style.background = iconColor;
|
|
||||||
icon.style.webkitBackgroundClip = 'text';
|
|
||||||
icon.style.backgroundClip = 'text';
|
|
||||||
icon.style.webkitTextFillColor = 'transparent';
|
|
||||||
icon.style.color = 'transparent';
|
|
||||||
} else {
|
|
||||||
icon.style.color = iconColor;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
icon.style.color = '#ffffff'; // Default White
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Remove System Styling (Module Overrides)
|
// 3. Remove System Styling (Module Overrides)
|
||||||
// Skip this for Foundryborne to keep default system look
|
// Skip this for Foundryborne to keep default system look (filters/brightness)
|
||||||
|
// UPDATE: We now WANT to override Foundryborne to apply our custom gradient, BUT we want to keep its filters!
|
||||||
if (colorTheme !== 'foundryborne') {
|
if (colorTheme !== 'foundryborne') {
|
||||||
icon.style.filter = 'none';
|
icon.style.filter = 'none';
|
||||||
icon.style.opacity = '1';
|
icon.style.opacity = '1';
|
||||||
|
|
||||||
icon.style.webkitTextFillColor = 'initial';
|
|
||||||
icon.style.backgroundClip = 'border-box';
|
|
||||||
icon.style.webkitBackgroundClip = 'border-box';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Handle Background Color
|
|
||||||
const isInactive = icon.classList.contains('inactive');
|
|
||||||
|
|
||||||
// Skip custom coloring for Foundryborne
|
|
||||||
if (colorTheme !== 'foundryborne') {
|
|
||||||
if (isInactive) {
|
|
||||||
icon.style.background = emptyColor;
|
|
||||||
icon.style.backgroundSize = 'cover'; // Reset size for empty
|
|
||||||
icon.style.backgroundPosition = 'center'; // Reset pos for empty
|
|
||||||
} else {
|
|
||||||
// Active
|
|
||||||
if (themeStart && themeEnd && totalIcons > 1) {
|
|
||||||
// Interpolate (Preset Themes)
|
|
||||||
const factor = index / (totalIcons - 1);
|
|
||||||
const color = interpolateColor(themeStart, themeEnd, factor);
|
|
||||||
icon.style.background = color;
|
|
||||||
icon.style.backgroundSize = 'cover';
|
|
||||||
icon.style.backgroundPosition = 'center';
|
|
||||||
} else {
|
|
||||||
// Custom Theme
|
|
||||||
// Check if fullColor appears to be a gradient
|
|
||||||
const isGradient = fullColor.includes('gradient');
|
|
||||||
|
|
||||||
if (isGradient && totalIcons > 0) {
|
// CSS Variables to be applied
|
||||||
icon.style.background = fullColor;
|
let cssBg = 'transparent';
|
||||||
icon.style.backgroundSize = `${totalIcons * 100}% 100%`;
|
let cssBgSize = 'cover';
|
||||||
|
let cssBgPos = 'center';
|
||||||
|
let cssIconColor = '#ffffff';
|
||||||
|
let cssIconBgSize = 'cover';
|
||||||
|
let cssIconBgPos = 'center';
|
||||||
|
|
||||||
// Calculate position
|
// 4. Handle Icon Color (Glyph)
|
||||||
// Leftmost (index 0) = 0%
|
if (!isSVG) {
|
||||||
// Rightmost (index total-1) = 100%
|
if (iconColor && iconColor !== '#ffffff') {
|
||||||
let pos = 0;
|
cssIconColor = iconColor;
|
||||||
if (totalIcons > 1) {
|
|
||||||
pos = (index / (totalIcons - 1)) * 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
icon.style.backgroundPosition = `${pos}% 0%`;
|
// Spanning Logic for Icon Gradient
|
||||||
icon.style.backgroundAttachment = 'local'; // Ensure it sticks to the specific element config if needed, though default is usually fine.
|
if (iconColor.includes('gradient') && totalIcons > 0) {
|
||||||
} else {
|
cssIconBgSize = `${totalIcons * 100}% 100%`;
|
||||||
// Solid Color
|
let pos = 0;
|
||||||
icon.style.background = fullColor;
|
if (totalIcons > 1) {
|
||||||
icon.style.backgroundSize = 'cover';
|
pos = (index / (totalIcons - 1)) * 100;
|
||||||
icon.style.backgroundPosition = 'center';
|
|
||||||
}
|
}
|
||||||
|
cssIconBgPos = `${pos}% 0%`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cssIconColor = '#ffffff';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Handle Background Color (Shape)
|
||||||
|
// Enable custom coloring for ALL themes now, but toggle classes selectively for Hybrid "Foundryborne"
|
||||||
|
|
||||||
|
// Always apply icon override for gradients
|
||||||
|
icon.classList.add('dh-fear-plus-icon-override');
|
||||||
|
|
||||||
|
if (colorTheme !== 'foundryborne') {
|
||||||
|
// Apply background override for all OTHER themes
|
||||||
|
icon.classList.add('dh-fear-plus-bg-override');
|
||||||
|
}
|
||||||
|
if (isInactive) {
|
||||||
|
cssBg = emptyColor;
|
||||||
|
cssBgSize = 'cover';
|
||||||
|
cssBgPos = 'center';
|
||||||
|
} else {
|
||||||
|
// Active
|
||||||
|
if (themeStart && themeEnd && totalIcons > 1) {
|
||||||
|
// Interpolate (Preset Themes)
|
||||||
|
const factor = index / (totalIcons - 1);
|
||||||
|
const color = interpolateColor(themeStart, themeEnd, factor);
|
||||||
|
|
||||||
|
// Apply Theme Color to BACKGROUND only
|
||||||
|
cssBg = color;
|
||||||
|
cssBgSize = 'cover';
|
||||||
|
cssBgPos = 'center';
|
||||||
|
} else {
|
||||||
|
// Custom Theme
|
||||||
|
// Check if fullColor appears to be a gradient
|
||||||
|
const isGradient = fullColor.includes('gradient');
|
||||||
|
|
||||||
|
if (isGradient && totalIcons > 0) {
|
||||||
|
cssBg = fullColor;
|
||||||
|
cssBgSize = `${totalIcons * 100}% 100%`;
|
||||||
|
|
||||||
|
// Calculate position
|
||||||
|
let pos = 0;
|
||||||
|
if (totalIcons > 1) {
|
||||||
|
pos = (index / (totalIcons - 1)) * 100;
|
||||||
|
}
|
||||||
|
cssBgPos = `${pos}% 0%`;
|
||||||
|
} else {
|
||||||
|
// Solid Color
|
||||||
|
cssBg = fullColor;
|
||||||
|
cssBgSize = 'cover';
|
||||||
|
cssBgPos = 'center';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 5. Handle Shape
|
// }
|
||||||
|
|
||||||
|
// 6. Handle Shape
|
||||||
let borderRadius = '50%';
|
let borderRadius = '50%';
|
||||||
if (iconShape === 'rounded') borderRadius = '20%';
|
if (iconShape === 'rounded') borderRadius = '20%';
|
||||||
else if (iconShape === 'square') borderRadius = '0%';
|
else if (iconShape === 'square') borderRadius = '0%';
|
||||||
|
|
||||||
icon.style.borderRadius = borderRadius;
|
// 7. Apply CSS Variables
|
||||||
|
icon.style.setProperty('--dh-fear-bg', cssBg);
|
||||||
|
icon.style.setProperty('--dh-fear-bg-size', cssBgSize);
|
||||||
|
icon.style.setProperty('--dh-fear-bg-pos', cssBgPos);
|
||||||
|
icon.style.setProperty('--dh-fear-icon-color', cssIconColor);
|
||||||
|
icon.style.setProperty('--dh-fear-icon-bg-size', cssIconBgSize);
|
||||||
|
icon.style.setProperty('--dh-fear-icon-bg-pos', cssIconBgPos);
|
||||||
|
icon.style.setProperty('--dh-fear-border-radius', borderRadius);
|
||||||
|
|
||||||
|
// Clean up direct styles that might interfere/confuse
|
||||||
|
icon.style.background = '';
|
||||||
|
icon.style.color = '';
|
||||||
|
icon.style.webkitBackgroundClip = '';
|
||||||
|
icon.style.backgroundClip = '';
|
||||||
|
icon.style.webkitTextFillColor = '';
|
||||||
|
icon.style.borderRadius = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove legacy container class if present
|
// Remove legacy container class if present
|
||||||
fearContainer.classList.remove('fear-tracker-plus-container-gradient');
|
fearContainer.classList.remove('fear-tracker-plus-container-gradient');
|
||||||
|
|
||||||
|
// Always clear container background to ensure our icon colors are visible and not obscured by system or previous styles
|
||||||
fearContainer.style.background = 'none';
|
fearContainer.style.background = 'none';
|
||||||
|
|
||||||
// Max Fear Animation
|
// Max Fear Animation
|
||||||
|
|
@ -554,15 +596,25 @@ function injectFearCustomization(html) {
|
||||||
|
|
||||||
// If totalIcons > 0 and activeIcons === totalIcons, apply animation
|
// If totalIcons > 0 and activeIcons === totalIcons, apply animation
|
||||||
if (totalIcons > 0 && activeIcons === totalIcons) {
|
if (totalIcons > 0 && activeIcons === totalIcons) {
|
||||||
|
// Prevent system "Blue" overrides
|
||||||
|
fearContainer.classList.remove('complete', 'max', 'full');
|
||||||
|
|
||||||
icons.forEach(icon => {
|
icons.forEach(icon => {
|
||||||
icon.classList.add('fear-tracker-plus-animate');
|
icon.classList.add('fear-tracker-plus-animate');
|
||||||
|
// Force filter reset to ensure our animation works and system color doesn't override
|
||||||
|
icon.style.filter = 'none';
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
icons.forEach(icon => {
|
icons.forEach(icon => {
|
||||||
icon.classList.remove('fear-tracker-plus-animate');
|
icon.classList.remove('fear-tracker-plus-animate');
|
||||||
|
// Restore filter if we touched it (only relevant if not custom theme)
|
||||||
|
if (colorTheme === 'foundryborne') {
|
||||||
|
icon.style.filter = '';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
icons.forEach(icon => icon.classList.remove('fear-tracker-plus-animate'));
|
icons.forEach(icon => icon.classList.remove('fear-tracker-plus-animate'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,56 @@
|
||||||
.fear-tracker-plus-gradient {
|
/* CSS Variable Defaults */
|
||||||
-webkit-background-clip: text;
|
:root {
|
||||||
background-clip: text;
|
--dh-fear-bg: transparent;
|
||||||
-webkit-text-fill-color: transparent;
|
--dh-fear-bg-size: cover;
|
||||||
display: inline-block;
|
--dh-fear-bg-pos: center;
|
||||||
/* Required for gradient text */
|
--dh-fear-icon-color: #ffffff;
|
||||||
|
--dh-fear-border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure the icon keeps its size and doesn't collapse */
|
/* Base Icon Styling */
|
||||||
#resource-fear i.fear-tracker-plus-custom {
|
#resource-fear i.fear-tracker-plus-custom {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
transition: all 0.5s ease;
|
transition: all 0.5s ease;
|
||||||
|
border-radius: var(--dh-fear-border-radius);
|
||||||
|
|
||||||
|
/* Background Shape Styling (Applied to container) - REMOVED */
|
||||||
|
|
||||||
|
/* Ensure icon is centered */
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Scoped Override for Custom Themes */
|
||||||
|
/* Scoped Override for Custom Themes */
|
||||||
|
#resource-fear i.fear-tracker-plus-custom.dh-fear-plus-bg-override {
|
||||||
|
background: var(--dh-fear-bg) !important;
|
||||||
|
background-size: var(--dh-fear-bg-size) !important;
|
||||||
|
background-position: var(--dh-fear-bg-pos) !important;
|
||||||
|
background-repeat: no-repeat !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icon Glyph Styling (Applied to ::before which contains the character) */
|
||||||
|
/* Icon Glyph Styling (Applied to ::before which contains the character) */
|
||||||
|
#resource-fear i.fear-tracker-plus-custom.dh-fear-plus-icon-override::before {
|
||||||
|
/* Apply Gradient or Solid Color to Text */
|
||||||
|
background: var(--dh-fear-icon-color);
|
||||||
|
background-size: var(--dh-fear-icon-bg-size, cover);
|
||||||
|
background-position: var(--dh-fear-icon-bg-pos, center);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
|
-webkit-background-clip: text !important;
|
||||||
|
background-clip: text !important;
|
||||||
|
-webkit-text-fill-color: transparent !important;
|
||||||
|
color: transparent !important;
|
||||||
|
|
||||||
|
/* Ensure it overlays correctly */
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@keyframes fearPulse {
|
@keyframes fearPulse {
|
||||||
0% {
|
0% {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
|
|
@ -29,10 +69,5 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#resource-fear i.fear-tracker-plus-animate {
|
#resource-fear i.fear-tracker-plus-animate {
|
||||||
display: inline-flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
transform-origin: center center;
|
|
||||||
animation: fearPulse 2s infinite ease-in-out;
|
animation: fearPulse 2s infinite ease-in-out;
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue