feat(inflexion): la curva que se dobla en el punto, sin fork
This commit is contained in:
1 parent
aa6b348674
commit
2296fa4868
2 files changed
+45
-50
No files matched your search
@@ -3,68 +3,46 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { useEnVista } from '@/lib/motion';
|
||||
|
||||
// A literal small illustration of the section's own idea: a business grows,
|
||||
// hits its inflection point (the dot), and from there either drifts flat
|
||||
// without direction (grey, dashed) or keeps climbing with a clear strategy
|
||||
// (dorado). Draws itself in once scrolled into view; a decorative aside, not
|
||||
// data, so it stays purely presentational.
|
||||
// The section's own subject, drawn: the inflection point itself. One line
|
||||
// rises gently (the present, azul), bends at the marked point, and from there
|
||||
// climbs steeply in dorado — clarity accelerating the climb. No fork, no
|
||||
// "deriva": that bifurcation belongs to ParaQuien, not here.
|
||||
const ANTES = 'M10 110 C 55 104, 85 88, 110 72';
|
||||
const DESPUES = 'M110 72 C 145 60, 178 24, 210 12';
|
||||
|
||||
export function IlustracionInflexion({ className = '' }: { className?: string }) {
|
||||
const { ref, enVista, reduced } = useEnVista();
|
||||
|
||||
// `motion.path` applies its own dash-based pathLength scaffolding even with
|
||||
// no initial/animate props, which left the lines invisible for
|
||||
// reduced-motion users — so that branch renders plain, non-motion <path>s
|
||||
// instead of trying to neutralise the animated ones.
|
||||
const punto = (
|
||||
<>
|
||||
<circle cx={110} cy={72} r={6} fill="currentColor" className="text-dorado" />
|
||||
<circle cx={110} cy={72} r={10} fill="none" stroke="currentColor" className="text-dorado" strokeWidth={1} strokeOpacity={0.4} />
|
||||
</>
|
||||
);
|
||||
|
||||
// Plain <path> under reduced motion — motion.path applies its own dash
|
||||
// scaffolding and would leave the drawing invisible.
|
||||
if (reduced) {
|
||||
return (
|
||||
<svg viewBox="0 0 320 180" className={className} aria-hidden="true" focusable="false">
|
||||
<path d="M20 140 C 70 150, 110 40, 165 60" fill="none" stroke="currentColor" className="text-azul-profundo" strokeWidth={3} strokeLinecap="round" />
|
||||
<path d="M165 60 C 210 76, 250 92, 300 88" fill="none" stroke="currentColor" className="text-gris-medio" strokeWidth={2.5} strokeLinecap="round" strokeDasharray="1 9" />
|
||||
<path d="M165 60 C 205 48, 245 8, 300 14" fill="none" stroke="currentColor" className="text-dorado" strokeWidth={3} strokeLinecap="round" />
|
||||
<circle cx="165" cy="60" r="6" fill="currentColor" className="text-dorado" />
|
||||
<circle cx="165" cy="60" r="10" fill="none" stroke="currentColor" className="text-dorado" strokeWidth={1} strokeOpacity={0.4} />
|
||||
<svg ref={ref} viewBox="0 0 220 120" className={className} aria-hidden="true" focusable="false">
|
||||
<path d={ANTES} fill="none" stroke="currentColor" className="text-azul-profundo" strokeWidth={2} strokeLinecap="round" />
|
||||
<path d={DESPUES} fill="none" stroke="currentColor" className="text-dorado" strokeWidth={3} strokeLinecap="round" />
|
||||
{punto}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
const draw = (delay: number) => ({
|
||||
const dibuja = (delay: number) => ({
|
||||
initial: { pathLength: 0 },
|
||||
animate: enVista ? { pathLength: 1 } : { pathLength: 0 },
|
||||
transition: { duration: 1.1, delay, ease: [0.22, 1, 0.36, 1] as const },
|
||||
});
|
||||
|
||||
return (
|
||||
<svg ref={ref} viewBox="0 0 320 180" className={className} aria-hidden="true" focusable="false">
|
||||
<motion.path
|
||||
d="M20 140 C 70 150, 110 40, 165 60"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
className="text-azul-profundo"
|
||||
strokeWidth={3}
|
||||
strokeLinecap="round"
|
||||
{...draw(0)}
|
||||
/>
|
||||
<motion.path
|
||||
d="M165 60 C 210 76, 250 92, 300 88"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
className="text-gris-medio"
|
||||
strokeWidth={2.5}
|
||||
strokeLinecap="round"
|
||||
strokeDasharray="1 9"
|
||||
{...draw(0.5)}
|
||||
/>
|
||||
<motion.path
|
||||
d="M165 60 C 205 48, 245 8, 300 14"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
className="text-dorado"
|
||||
strokeWidth={3}
|
||||
strokeLinecap="round"
|
||||
{...draw(0.5)}
|
||||
/>
|
||||
<circle cx="165" cy="60" r="6" fill="currentColor" className="text-dorado" />
|
||||
<circle cx="165" cy="60" r="10" fill="none" stroke="currentColor" className="text-dorado" strokeWidth={1} strokeOpacity={0.4} />
|
||||
<svg ref={ref} viewBox="0 0 220 120" className={className} aria-hidden="true" focusable="false">
|
||||
<motion.path d={ANTES} fill="none" stroke="currentColor" className="text-azul-profundo" strokeWidth={2} strokeLinecap="round" {...dibuja(0)} />
|
||||
<motion.path d={DESPUES} fill="none" stroke="currentColor" className="text-dorado" strokeWidth={3} strokeLinecap="round" {...dibuja(0.5)} />
|
||||
{punto}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -5,16 +5,33 @@ import { IlustracionInflexion } from '../IlustracionInflexion';
|
||||
describe('<IlustracionInflexion />', () => {
|
||||
it('is decorative: hidden from assistive tech', () => {
|
||||
const { container } = render(<IlustracionInflexion />);
|
||||
expect(container.querySelector('svg')).toHaveAttribute('aria-hidden', 'true');
|
||||
const svg = container.querySelector('svg');
|
||||
expect(svg).toHaveAttribute('aria-hidden', 'true');
|
||||
expect(svg).toHaveAttribute('focusable', 'false');
|
||||
});
|
||||
|
||||
it('draws the growth line, the drift-away line and the strategy line', () => {
|
||||
it('shares the unified canvas with the other scene illustrations', () => {
|
||||
const { container } = render(<IlustracionInflexion />);
|
||||
expect(container.querySelectorAll('path')).toHaveLength(3);
|
||||
expect(container.querySelector('svg')).toHaveAttribute('viewBox', '0 0 220 120');
|
||||
});
|
||||
|
||||
it('is a single bend at the inflection point, not a fork', () => {
|
||||
const { container } = render(<IlustracionInflexion />);
|
||||
// The previous version drew three diverging paths (growth / drift /
|
||||
// strategy) — the same fork as ParaQuien. The rework is one continuous
|
||||
// line that bends at the point: two segments, pre- and post-inflection.
|
||||
expect(container.querySelectorAll('path')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('marks the inflection point itself', () => {
|
||||
const { container } = render(<IlustracionInflexion />);
|
||||
expect(container.querySelectorAll('circle').length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it('bends from the present (azul) into clarity (dorado)', () => {
|
||||
const { container } = render(<IlustracionInflexion />);
|
||||
const clases = Array.from(container.querySelectorAll('path')).map((p) => p.getAttribute('class') ?? '');
|
||||
expect(clases.some((c) => c.includes('text-azul-profundo'))).toBe(true);
|
||||
expect(clases.some((c) => c.includes('text-dorado'))).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user