Skip to content

Commit 3b3fef9

Browse files
committed
Fix CSS calculation & densities
1 parent ca19506 commit 3b3fef9

File tree

9 files changed

+106
-20
lines changed

9 files changed

+106
-20
lines changed

src/Respimgcss/Application/Model/SourceSizeMediaCondition.php

+7-3
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,14 @@ protected function initializeCondition(
136136
// Minimum value
137137
if ($modifier == CssMinMaxMediaConditionInterface::MIN) {
138138
$this->initializeMinProperty($minProperty, $value);
139+
139140
return;
140141
}
141142

142143
// Maximum value
143144
if ($modifier == CssMinMaxMediaConditionInterface::MAX) {
144145
$this->initializeMaxProperty($maxProperty, $value);
146+
145147
return;
146148
}
147149

@@ -195,18 +197,20 @@ public function getConditions(): array
195197
*
196198
* @param AbsoluteLengthInterface $width Width
197199
* @param float $density Density
200+
* @param int|null $lastMinimumWidth Minimum width of the next higher breakpoint
198201
*
199202
* @return bool This source size condition matches
200203
*/
201-
public function matches(AbsoluteLengthInterface $width, float $density): bool
204+
public function matches(AbsoluteLengthInterface $width, float $density, int $lastMinimumWidth = null): bool
202205
{
203206
$match = true;
204207

205208
// Run through all conditions
206209
/** @var CssMinMaxMediaConditionInterface $condition */
207210
foreach ($this->conditions as $condition) {
208-
$value = ($condition instanceof WidthMediaCondition) ? $width->getValue() : $density;
209-
if (!$condition->matches($value)) {
211+
$rangeLower = ($condition instanceof WidthMediaCondition) ? ($width->getValue() * $density) : $density;
212+
$rangeUpper = ($lastMinimumWidth === null) ? $rangeLower : ($lastMinimumWidth * $density - 1);
213+
if (!$condition->matches($rangeLower) || !$condition->matches($rangeUpper)) {
210214
$match = false;
211215
break;
212216
}

src/Respimgcss/Domain/Service/AbstractCssRulesetCompilerService.php

+37-2
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,44 @@ public function __construct(
9090
LengthFactoryInterface $lengthFactory
9191
) {
9292
$this->cssRuleset = $cssRuleset;
93-
$this->breakpoints = $breakpoints;
9493
$this->imageCandidates = $imageCandidates;
9594
$this->lengthFactory = $lengthFactory;
96-
array_unshift($this->breakpoints, $this->lengthFactory->createAbsoluteLength(0));
95+
$this->breakpoints = $this->prepareBreakpoints($breakpoints);
96+
}
97+
98+
/**
99+
* Prepare the list of breakpoints
100+
*
101+
* @param AbsoluteLengthInterface[] $breakpoints Breakpoints
102+
*
103+
* @return AbsoluteLengthInterface[] Prepared breakpoints
104+
*/
105+
protected function prepareBreakpoints(array $breakpoints): array
106+
{
107+
usort($breakpoints, [$this, 'sortBreakpoints']);
108+
109+
// Add a virtual zero-width breakpoint to the beginning of the list if necessary
110+
if (count($breakpoints) && ($breakpoints[0]->getValue() != 0)) {
111+
array_unshift($breakpoints, $this->lengthFactory->createAbsoluteLength(0));
112+
}
113+
114+
return $breakpoints;
115+
}
116+
117+
/**
118+
* Sort two breakpoints by size
119+
*
120+
* @param AbsoluteLengthInterface $breakpoint1 Breakpoint 1
121+
* @param AbsoluteLengthInterface $breakpoint2 Breakpoint 2
122+
*
123+
* @return int Sorting
124+
*/
125+
protected function sortBreakpoints(AbsoluteLengthInterface $breakpoint1, AbsoluteLengthInterface $breakpoint2): int
126+
{
127+
if ($breakpoint1->getValue() == $breakpoint2->getValue()) {
128+
return 0;
129+
}
130+
131+
return ($breakpoint1->getValue() > $breakpoint2->getValue()) ? 1 : -1;
97132
}
98133
}

src/Respimgcss/Domain/Service/WidthCssRulesetCompilerService.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,10 @@ public function compile(float $density): CssRulesetInterface
107107
*/
108108
protected function compileForSourceSizes(float $density): void
109109
{
110-
// Run through all breakpoints
110+
// Run through all breakpoints (from lowest to highest)
111111
/** @var AbsoluteLengthInterface $breakpoint */
112112
foreach ($this->breakpoints as $breakpoint) {
113+
// Try to find a matching image candidate
113114
/** @scrutinizer ignore-call */
114115
$imageCandidateMatch = $this->sourceSizeList->findImageCandidate(
115116
$this->imageCandidates,

src/Respimgcss/Infrastructure/Generator.php

+10-4
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ public function make(array $densities = [1], string $sizes = ''): CssRulesetInte
133133
$cssRuleset = new CssRuleset();
134134

135135
// If all necessary properties are given
136-
if (count($this->breakpoints) && count($this->getImageCandidates()) && count($densities)) {
137-
$cssRuleset = $this->compileCssRuleset($cssRuleset, $densities, $sizes);
136+
if (count($this->getImageCandidates())) {
137+
$cssRuleset = $this->compileCssRuleset($cssRuleset, count($densities) ? $densities : [1], $sizes);
138138
}
139139

140140
return $cssRuleset;
@@ -218,7 +218,13 @@ function ($unparsedSourceSize) use ($sourceSizeFactory) {
218218
},
219219
$unparsedSourceSizes
220220
);
221-
$legthFactory = new LengthFactory(new ViewportCalculatorServiceFactory(), $this->emPixel);
222-
return count($sourceSizes) ? new SourceSizeList($sourceSizes, $legthFactory) : null;
221+
if (count($sourceSizes)) {
222+
return new SourceSizeList(
223+
$sourceSizes,
224+
new LengthFactory(new ViewportCalculatorServiceFactory(), $this->emPixel)
225+
);
226+
}
227+
228+
return null;
223229
}
224230
}

src/Respimgcss/Infrastructure/SourceSizeList.php

+8-3
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,18 @@ public function findImageCandidate(
109109
float $density
110110
): ?SourceSizeImageCandidateMatch {
111111

112-
// Run through the source sizes
112+
// Run through the source sizes (from biggest to smallest)
113113
/** @var SourceSize $sourceSize */
114114
$lastMinimumWidth = null;
115115
foreach ($this->getArrayCopy() as $sourceSize) {
116116
$mediaCondition = $sourceSize->getMediaCondition();
117-
if ($mediaCondition->matches($breakpoint, $density)) {
117+
118+
// If the current breakpoint and density matches the source size condition
119+
if ($mediaCondition->matches($breakpoint, $density, $lastMinimumWidth)) {
118120
return $this->findImageCandidateForSourceSize(
119121
$sourceSize,
120122
$imageCandidates,
123+
$density,
121124
$breakpoint,
122125
$this->getSourceSizeMaximumWidth($mediaCondition, $lastMinimumWidth)
123126
);
@@ -133,6 +136,7 @@ public function findImageCandidate(
133136
*
134137
* @param SourceSize $sourceSize Matching source size
135138
* @param ImageCandidateSetInterface $imageCandidates Image candidates
139+
* @param float $density Density
136140
* @param AbsoluteLengthInterface $minWidth Minimum viewport width
137141
* @param AbsoluteLengthInterface|null $maxWidth Maximum viewport width
138142
*
@@ -141,6 +145,7 @@ public function findImageCandidate(
141145
protected function findImageCandidateForSourceSize(
142146
SourceSize $sourceSize,
143147
ImageCandidateSetInterface $imageCandidates,
148+
float $density,
144149
AbsoluteLengthInterface $minWidth,
145150
AbsoluteLengthInterface $maxWidth = null
146151
): ?SourceSizeImageCandidateMatch {
@@ -153,7 +158,7 @@ protected function findImageCandidateForSourceSize(
153158
return $this->findImageCandidateForMinImageWidth(
154159
$sourceSize,
155160
$imageCandidates,
156-
max($sourceSize->getValue()->getValue($minWidth), $sourceSize->getValue()->getValue($maxWidth))
161+
max($sourceSize->getValue()->getValue($minWidth), $sourceSize->getValue()->getValue($maxWidth)) * $density
157162
);
158163
}
159164

src/Respimgcss/Ports/Generator.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class Generator extends \Jkphl\Respimgcss\Infrastructure\Generator
5454
*
5555
* @api
5656
*/
57-
public function __construct($breakpoints, int $emPixel = 16)
57+
public function __construct(array $breakpoints = [], int $emPixel = 16)
5858
{
5959
parent::__construct($breakpoints, $emPixel);
6060
}

src/Respimgcss/Tests/Application/SourceSizeMediaConditionTest.php

+6-6
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ public function testSourceSizesMinMaxMediaConditions()
7474
$this->assertEquals(2, $sourceSizeMediaCondition->getMinimumResolution());
7575
$this->assertEquals(3, $sourceSizeMediaCondition->getMaximumResolution());
7676

77-
$this->assertTrue($sourceSizeMediaCondition->matches(new AbsoluteLength(150), 2.5));
78-
$this->assertFalse($sourceSizeMediaCondition->matches(new AbsoluteLength(50), 2.5));
79-
$this->assertFalse($sourceSizeMediaCondition->matches(new AbsoluteLength(250), 2.5));
77+
$this->assertTrue($sourceSizeMediaCondition->matches(new AbsoluteLength(60), 2.5));
78+
$this->assertFalse($sourceSizeMediaCondition->matches(new AbsoluteLength(20), 2.5));
79+
$this->assertFalse($sourceSizeMediaCondition->matches(new AbsoluteLength(100), 2.5));
8080
$this->assertFalse($sourceSizeMediaCondition->matches(new AbsoluteLength(150), 1));
8181
$this->assertFalse($sourceSizeMediaCondition->matches(new AbsoluteLength(150), 4));
8282
$this->assertFalse($sourceSizeMediaCondition->matches(new AbsoluteLength(50), 1));
@@ -134,8 +134,8 @@ public function testSourceSizesEqualsMediaConditions()
134134
$this->assertEquals(100, $sourceSizeMediaCondition->getMaximumWidth());
135135
$this->assertEquals(2, $sourceSizeMediaCondition->getMinimumResolution());
136136
$this->assertEquals(2, $sourceSizeMediaCondition->getMaximumResolution());
137-
$this->assertTrue($sourceSizeMediaCondition->matches(new AbsoluteLength(100), 2));
138-
$this->assertFalse($sourceSizeMediaCondition->matches(new AbsoluteLength(50), 2));
139-
$this->assertFalse($sourceSizeMediaCondition->matches(new AbsoluteLength(100), 3));
137+
$this->assertTrue($sourceSizeMediaCondition->matches(new AbsoluteLength(50), 2));
138+
$this->assertFalse($sourceSizeMediaCondition->matches(new AbsoluteLength(100), 2));
139+
$this->assertFalse($sourceSizeMediaCondition->matches(new AbsoluteLength(50), 3));
140140
}
141141
}

src/Respimgcss/Tests/Fixture/Css/WidthImageCandidatesSizes.css

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Respimgcss/Tests/Infrastructure/GeneratorTest.php

+29
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,33 @@ protected function runGeneratorWidthImageCandidatesAssertions(InternalGenerator
131131
$this->assertTrue(is_string($css));
132132
$this->assertStringEqualsFile(dirname(__DIR__).'/Fixture/Css/WidthImageCandidates.css', $css);
133133
}
134+
135+
/**
136+
* Test the internal generator with width based image candidates
137+
*/
138+
public function testGeneratorWidthImageCandidatesSourceSizes()
139+
{
140+
$generator = new Generator(['400px', '1200px'], 16);
141+
$this->assertInstanceOf(InternalGenerator::class, $generator);
142+
$this->runGeneratorWidthImageCandidatesSourceSizesAssertions($generator);
143+
}
144+
145+
/**
146+
* Run the generator assertions for density based image candidates
147+
*
148+
* @param InternalGenerator $generator
149+
*/
150+
protected function runGeneratorWidthImageCandidatesSourceSizesAssertions(InternalGenerator $generator)
151+
{
152+
$generator->registerImageCandidate('small-400.jpg 400w');
153+
$generator->registerImageCandidate('medium-800.jpg', '800w');
154+
$generator->registerImageCandidate('large-1200.jpg', '1200w');
155+
$generator->registerImageCandidate('extralarge-1600.jpg', '1600w');
156+
157+
$cssRuleset = $generator->make([1, 2], '(min-width: 400px) 50vw, (min-width: 1200px) 33.333vw, 100vw');
158+
$this->assertInstanceOf(CssRulesetInterface::class, $cssRuleset);
159+
$css = $cssRuleset->toCss('.example');
160+
$this->assertTrue(is_string($css));
161+
$this->assertStringEqualsFile(dirname(__DIR__).'/Fixture/Css/WidthImageCandidatesSizes.css', $css);
162+
}
134163
}

0 commit comments

Comments
 (0)