feat: Added repeat option to disable loop (#181)

pull/184/head
Jonah Lawrence 2 years ago committed by GitHub
parent d65ccb2566
commit ebec94d5d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -98,6 +98,7 @@ Feel free to [open a PR](https://github.com/DenverCoder1/readme-typing-svg/issue
| `multiline` | `true` to wrap lines or `false` to retype on one line (default: `false`) | boolean | `true` or `false` |
| `duration` | Duration of the printing of a single line in milliseconds (default: `5000`) | integer | Any positive number |
| `pause` | Duration of the pause between lines in milliseconds (default: `0`) | integer | Any non-negative number |
| `repeat` | `true` to loop around to the first line after the last (default: `true`) | boolean | `true` or `false` |
## 📤 Deploying it on your own

@ -83,23 +83,29 @@
<input class="param jscolor jscolor-active" id="background" name="background" alt="Background color" data-jscolor="{ format: 'hexa' }" value="#00000000">
<label for="center">Horizontally Centered</label>
<select class="param" id="center" name="center" alt="Horizontally Centered" value="false">
<select class="param" id="center" name="center" alt="Horizontally Centered">
<option>false</option>
<option>true</option>
</select>
<label for="vCenter">Vertically Centered</label>
<select class="param" id="vCenter" name="vCenter" alt="Vertically Centered" value="false">
<select class="param" id="vCenter" name="vCenter" alt="Vertically Centered">
<option>false</option>
<option>true</option>
</select>
<label for="multiline">Multiline</label>
<select class="param" id="multiline" name="multiline" alt="Multiline" value="false">
<select class="param" id="multiline" name="multiline" alt="Multiline">
<option value="false">Type sentences on one line</option>
<option value="true">Each sentence on a new line</option>
</select>
<label for="repeat">Repeat</label>
<select class="param" id="repeat" name="repeat" alt="Repeat">
<option>true</option>
<option>false</option>
</select>
<label for="dimensions" title="Width ✕ Height">Width ✕ Height</label>
<span id="dimensions">
<input class="param inline" type="number" id="width" name="width" alt="Width (px)" placeholder="435" value="435">

@ -13,6 +13,7 @@ let preview = {
height: "50",
duration: "5000",
pause: "0",
repeat: "true",
},
dummyText: [
"The five boxing wizards jump quickly",

@ -46,6 +46,9 @@ class RendererModel
/** @var int $pause pause duration between lines in milliseconds */
public $pause;
/** @var bool $repeat Whether to loop around to the first line after the last */
public $repeat;
/** @var string $fontCSS CSS required for displaying the selected font */
public $fontCSS;
@ -66,6 +69,7 @@ class RendererModel
"multiline" => "false",
"duration" => "5000",
"pause" => "0",
"repeat" => "true",
];
/**
@ -90,6 +94,7 @@ class RendererModel
$this->multiline = $this->checkBoolean($params["multiline"] ?? $this->DEFAULTS["multiline"]);
$this->duration = $this->checkNumberPositive($params["duration"] ?? $this->DEFAULTS["duration"], "duration");
$this->pause = $this->checkNumberNonNegative($params["pause"] ?? $this->DEFAULTS["pause"], "pause");
$this->repeat = $this->checkBoolean($params["repeat"] ?? $this->DEFAULTS["repeat"]);
$this->fontCSS = $this->fetchFontCSS($this->font, $this->weight, $params["lines"]);
}

@ -5,43 +5,62 @@
style='background-color: <?= $background ?>;'
width='<?= $width ?>px' height='<?= $height ?>px'>
<?= preg_replace("/\n/", "\n\t", $fontCSS) ?>
<?= $fontCSS ?>
<?php $previousId = "d" . (count($lines) - 1); ?>
<?php for ($i = 0; $i < count($lines); ++$i): ?>
<?php $lastLineIndex = count($lines) - 1; ?>
<?php for ($i = 0; $i <= $lastLineIndex; ++$i): ?>
<path id='path<?= $i ?>'>
<?php if (!$multiline): ?>
<animate id='d<?= $i ?>' attributeName='d' begin='<?= ($i == 0 ? "0s;" : "") .
$previousId ?>.end' dur='<?= $duration + $pause ?>ms'
values='m0,<?= $height / 2 ?> h0 ; m0,<?= $height / 2 ?> h<?= $width ?> ; m0,<?= $height /
2 ?> h<?= $width ?> ; m0,<?= $height / 2 ?> h0'
keyTimes='0;<?= (0.8 * $duration) / ($duration + $pause) ?>;<?= (0.8 * $duration + $pause) /
($duration + $pause) ?>;1' />
<?php else: ?>
<?php $lineHeight = $size + 5; ?>
<animate id='d<?= $i ?>' attributeName='d' dur='<?= ($duration + $pause) * ($i + 1) ?>ms' fill="freeze"
begin='0s;<?= "d" . (count($lines) - 1) ?>.end' keyTimes='0;<?= $i / ($i + 1) ?>;<?= $i / ($i + 1) +
$duration / (($duration + $pause) * ($i + 1)) ?>;1'
values='m0,<?= ($i + 1) * $lineHeight ?> h0 ; m0,<?= ($i + 1) * $lineHeight ?> h0 ; m0,<?= ($i + 1) *
$lineHeight ?> h<?= $width ?> ; m0,<?= ($i + 1) * $lineHeight ?> h<?= $width ?>' />
<?php endif; ?>
<?php if (!$multiline): ?>
<!-- Single line -->
<?php
// start after previous line
$begin = "d" . ($i - 1) . ".end";
if ($i == 0) {
// if this is the first line, start at 0 seconds
// and also after the last line if repeat is true
$begin = $repeat ? "0s;d$lastLineIndex.end" : "0s";
}
// don't delete text after typing the last line if repeat is false
$freeze = !$repeat && $i == $lastLineIndex;
// empty line values
$yOffset = $height / 2;
$emptyLine = "m0,$yOffset h0";
$fullLine = "m0,$yOffset h$width";
$values = [$emptyLine, $fullLine, $fullLine, $freeze ? $fullLine : $emptyLine];
// keyTimes for the animation
$keyTimes = [
"0",
(0.8 * $duration) / ($duration + $pause),
(0.8 * $duration + $pause) / ($duration + $pause),
"1",
];
?>
<animate id='d<?= $i ?>' attributeName='d' begin='<?= $begin ?>'
dur='<?= $duration + $pause ?>ms' fill='<?= $freeze ? "freeze" : "remove" ?>'
values='<?= implode(" ; ", $values) ?>' keyTimes='<?= implode(";", $keyTimes) ?>' />
<?php else: ?>
<!-- Multiline -->
<?php
$nextIndex = $i + 1;
$lineHeight = $size + 5;
$lineDuration = ($duration + $pause) * $nextIndex;
$yOffset = $nextIndex * $lineHeight;
$emptyLine = "m0,$yOffset h0";
$fullLine = "m0,$yOffset h$width";
$values = [$emptyLine, $emptyLine, $fullLine, $fullLine];
$keyTimes = ["0", $i / $nextIndex, $i / $nextIndex + $duration / $lineDuration, "1"];
?>
<animate id='d<?= $i ?>' attributeName='d' begin='0s<?= $repeat ? ";d$lastLineIndex.end" : "" ?>'
dur='<?= $lineDuration ?>ms' fill="freeze"
values='<?= implode(" ; ", $values) ?>' keyTimes='<?= implode(";", $keyTimes) ?>' />
<?php endif; ?>
</path>
<text font-family='"<?= $font ?>", monospace' fill='<?= $color ?>' font-size='<?= $size ?>'
<?php if ($vCenter): ?>
dominant-baseline='middle'
<?php else: ?>
dominant-baseline='auto'
<?php endif; ?>
<?php if ($center): ?>
x='50%' text-anchor='middle'>
<?php else: ?>
x='0%' text-anchor='start'>
<?php endif; ?>
dominant-baseline='<?= $vCenter ? "middle" : "auto" ?>'
x='<?= $center ? "50%" : "0%" ?>' text-anchor='<?= $center ? "middle" : "start" ?>'>
<textPath xlink:href='#path<?= $i ?>'>
<?= $lines[$i] . "\n" ?>
</textPath>
</text>
<?php $previousId = "d" . $i; ?>
<?php endfor; ?>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

@ -40,6 +40,7 @@ class RendererView
"fontCSS" => $this->model->fontCSS,
"duration" => $this->model->duration,
"pause" => $this->model->pause,
"repeat" => $this->model->repeat,
]);
// render SVG with output buffering
ob_start();

@ -287,4 +287,33 @@ final class OptionsTest extends TestCase
];
print_r(new RendererModel("src/templates/main.php", $params));
}
/**
* Test repeat set to true, false, other
*/
public function testRepeat(): void
{
// not set
$params = [
"lines" => "text",
];
$model = new RendererModel("src/templates/main.php", $params);
$this->assertEquals(true, $model->repeat);
// true
$params = [
"lines" => "text",
"repeat" => "true",
];
$model = new RendererModel("src/templates/main.php", $params);
$this->assertEquals(true, $model->repeat);
// other / false
$params = [
"lines" => "text",
"repeat" => "other",
];
$model = new RendererModel("src/templates/main.php", $params);
$this->assertEquals(false, $model->repeat);
}
}

@ -8,6 +8,24 @@ require "vendor/autoload.php";
final class RendererTest extends TestCase
{
/**
* Static method to assert strings are equal while ignoring whitespace
*
* @param string $expected
* @param string $actual
*/
public static function compareNoCommentsOrWhitespace(string $expected, string $actual)
{
// remove comments and whitespace
$expected = preg_replace("/\s+/", " ", preg_replace("/<!--.*?-->/s", " ", $expected));
$actual = preg_replace("/\s+/", " ", preg_replace("/<!--.*?-->/s", " ", $actual));
// add newlines to make it easier to debug
$expected = str_replace(">", ">\n", $expected);
$actual = str_replace(">", ">\n", $actual);
// assert strings are equal
self::assertSame($expected, $actual);
}
/**
* Test normal card render
*/
@ -26,7 +44,9 @@ final class RendererTest extends TestCase
"height" => "50",
];
$controller = new RendererController($params);
$this->assertStringEqualsFile("tests/svg/test_normal.svg", $controller->render());
$expectedSVG = file_get_contents("tests/svg/test_normal.svg");
$actualSVG = $controller->render();
$this->compareNoCommentsOrWhitespace($expectedSVG, $actualSVG);
}
public function testMultilineCardRender(): void
@ -134,7 +154,9 @@ final class RendererTest extends TestCase
"height" => "50",
];
$controller = new RendererController($params);
$this->assertStringEqualsFile("tests/svg/test_normal.svg", $controller->render());
$expectedSVG = file_get_contents("tests/svg/test_normal.svg");
$actualSVG = $controller->render();
$this->compareNoCommentsOrWhitespace($expectedSVG, $actualSVG);
}
/**
@ -205,4 +227,57 @@ final class RendererTest extends TestCase
$this->assertStringContainsString("dur='12000ms'", $controller->render());
$this->assertStringContainsString("keyTimes='0;0.5;0.91666666666667;1'", $controller->render());
}
/**
* Test repeat set to false
*/
public function testRepeatFalse(): void
{
$params = [
"lines" => implode(";", [
"Full-stack web and app developer",
"Self-taught UI/UX Designer",
"10+ years of coding experience",
"Always learning new things",
]),
"center" => "true",
"vCenter" => "true",
"width" => "380",
"height" => "50",
"repeat" => "false",
];
$controller = new RendererController($params);
$actualSVG = preg_replace("/\s+/", " ", $controller->render());
$this->assertStringContainsString("begin='0s'", $actualSVG);
$this->assertStringContainsString(
"begin='d2.end' dur='5000ms' fill='freeze' values='m0,25 h0 ; m0,25 h380 ; m0,25 h380 ; m0,25 h380'",
$actualSVG
);
$this->assertStringNotContainsString(";d3.end", $actualSVG);
}
/**
* Test repeat set to false on multiline card
*/
public function testRepeatFalseMultiline(): void
{
$params = [
"lines" => implode(";", [
"Full-stack web and app developer",
"Self-taught UI/UX Designer",
"10+ years of coding experience",
"Always learning new things",
]),
"center" => "true",
"vCenter" => "true",
"width" => "380",
"height" => "200",
"multiline" => "true",
"repeat" => "false",
];
$controller = new RendererController($params);
$actualSVG = preg_replace("/\s+/", " ", $controller->render());
$this->assertStringContainsString("begin='0s'", $actualSVG);
$this->assertStringNotContainsString(";d3.end", $actualSVG);
}
}

@ -5,9 +5,9 @@
style='background-color: #00000000;'
width='380px' height='50px'>
<path id='path0'>
<animate id='d0' attributeName='d' begin='0s;d3.end' dur='5000ms'
<animate id='d0' attributeName='d' begin='0s;d3.end'
dur='5000ms' fill='remove'
values='m0,25 h0 ; m0,25 h380 ; m0,25 h380 ; m0,25 h0'
keyTimes='0;0.8;0.8;1' />
</path>
@ -20,7 +20,8 @@
</text>
<path id='path1'>
<animate id='d1' attributeName='d' begin='d0.end' dur='5000ms'
<animate id='d1' attributeName='d' begin='d0.end'
dur='5000ms' fill='remove'
values='m0,25 h0 ; m0,25 h380 ; m0,25 h380 ; m0,25 h0'
keyTimes='0;0.8;0.8;1' />
</path>
@ -33,7 +34,8 @@
</text>
<path id='path2'>
<animate id='d2' attributeName='d' begin='d1.end' dur='5000ms'
<animate id='d2' attributeName='d' begin='d1.end'
dur='5000ms' fill='remove'
values='m0,25 h0 ; m0,25 h380 ; m0,25 h380 ; m0,25 h0'
keyTimes='0;0.8;0.8;1' />
</path>
@ -46,7 +48,8 @@
</text>
<path id='path3'>
<animate id='d3' attributeName='d' begin='d2.end' dur='5000ms'
<animate id='d3' attributeName='d' begin='d2.end'
dur='5000ms' fill='remove'
values='m0,25 h0 ; m0,25 h380 ; m0,25 h380 ; m0,25 h0'
keyTimes='0;0.8;0.8;1' />
</path>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Loading…
Cancel
Save