Last active
June 3, 2021 13:56
-
-
Save antonlukin/9347142e08ef725227fca08b8047823b to your computer and use it in GitHub Desktop.
Upload canvas poster and display custom sharing urls.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Canvas saver</title> | |
</head> | |
<body> | |
<canvas id="canvas" width="600" height="315"></canvas> | |
<p> | |
<button id="button">Сохранить постер</button> | |
</p> | |
<p id="content"></p> | |
<script> | |
var canvas = document.getElementById('canvas'); | |
var context = canvas.getContext('2d'); | |
context.fillStyle = "#ddd"; | |
context.fillRect(0, 0, canvas.width, canvas.height); | |
// begin custom shape | |
context.beginPath(); | |
context.moveTo(170, 80); | |
context.bezierCurveTo(130, 100, 130, 150, 230, 150); | |
context.bezierCurveTo(250, 180, 320, 180, 340, 150); | |
context.bezierCurveTo(420, 150, 420, 120, 390, 100); | |
context.bezierCurveTo(430, 40, 370, 30, 340, 50); | |
context.bezierCurveTo(320, 5, 250, 20, 250, 50); | |
context.bezierCurveTo(200, 5, 150, 20, 170, 80); | |
// complete custom shape | |
context.closePath(); | |
context.lineWidth = 5; | |
context.fillStyle = '#8ED6FF'; | |
context.fill(); | |
context.strokeStyle = 'blue'; | |
context.stroke(); | |
var button = document.getElementById('button'); | |
var result = document.getElementById('result'); | |
button.addEventListener('click', function(e) { | |
e.preventDefault(); | |
button.textContent = 'Загрузка...'; | |
content.innerHTML = ''; | |
var formData = new FormData(); | |
formData.append('poster', canvas.toDataURL()); | |
var request = new XMLHttpRequest(); | |
request.open( 'POST', '/share/upload/' ); | |
request.responseType = 'json'; | |
request.addEventListener('readystatechange', function() { | |
if (request.readyState === 4) { | |
button.textContent = 'Сохранить постер'; | |
} | |
}); | |
request.addEventListener('load', function() { | |
var response = request.response || {}; | |
if (!response.success) { | |
return alert('error'); | |
} | |
var link = document.createElement('a'); | |
link.href = response.data.link; | |
link.textContent = response.data.link; | |
link.target = '_blank'; | |
content.appendChild(link); | |
var image = document.createElement('img'); | |
image.src = response.data.poster; | |
image.style.display = 'block'; | |
image.style.marginTop = '20px'; | |
content.appendChild(image); | |
}); | |
request.addEventListener('error', function() { | |
alert('error'); | |
}); | |
request.send(formData); | |
}); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Tigers\Dvor; | |
/** | |
* Upload and save canvas posters to show them as sharing results. | |
* | |
* @author Anton Lukin | |
* @version 1.0 | |
* @license MIT License | |
*/ | |
final class Sharing | |
{ | |
/** | |
* Url to project home page. | |
*/ | |
public $url = 'https://example.org'; | |
/** | |
* Redirect page to desired location; | |
* | |
* @param string $location New page location. | |
* @param int $status HTTP header status. By default: 301. | |
*/ | |
private function redirect_page($location, $status = 301) { | |
header("Location: {$location}", true, $status); | |
exit; | |
} | |
/** | |
* Generate unique poster name. | |
*/ | |
private function get_unique_name() { | |
return sha1(uniqid('', true)); | |
} | |
/** | |
* Decode base64 image from POST request. | |
* | |
* @param string $image Base64 encoded png image. | |
*/ | |
private function decode_image($image) { | |
return base64_decode(str_replace('data:image/png;base64,', '', $image)); | |
} | |
/** | |
* Send JSON and exit. | |
* | |
* @param int $status HTTP status code. | |
* @param array $output Data to response. | |
*/ | |
private function send_json($output, $status) { | |
http_response_code($status); | |
header('Content-Type: application/json'); | |
echo json_encode($output); | |
exit; | |
} | |
/** | |
* Send JSON width error message. | |
* | |
* @param mixed $data Error data. | |
* @param int $status HTTP status code. By default: 500. | |
*/ | |
private function send_json_error($data, $status = 500) { | |
$output = array( | |
'success' => false, | |
'data' => $data, | |
); | |
return $this->send_json($output, $status); | |
} | |
/** | |
* Send JSON with success message. | |
* | |
* @param mixed $data Success data. | |
* @param int $status HTTP status code. By default: 500. | |
*/ | |
private function send_json_success($data) { | |
$output = array( | |
'success' => true, | |
'data' => $data, | |
); | |
return $this->send_json($output, 200); | |
} | |
/** | |
* Upload poster from js FormData. | |
*/ | |
private function upload_poster() { | |
if (empty($_POST['poster'])) { | |
$this->send_json_error('The poster field should be defined', 400); | |
} | |
$image = $this->decode_image($_POST['poster']); | |
if (false === $image) { | |
$this->send_json_error('Wrong base64 image format', 400); | |
} | |
$name = $this->get_unique_name(); | |
// Try to save poster. | |
$file = file_put_contents(__DIR__ . "/posters/{$name}.png", $image); | |
if (false === $file) { | |
$this->send_json_error('Failed to save poster'); | |
} | |
$data = array( | |
'link' => $this->url . "/share/{$name}/", | |
'poster' => $this->url . "/share/posters/{$name}.png", | |
); | |
$this->send_json_success($data); | |
} | |
/** | |
* Show tags template. | |
* | |
* @param string $name Poster name. | |
*/ | |
private function show_tags($name) { | |
if (!file_exists(__DIR__ . "/posters/{$name}.png")) { | |
$this->redirect_page($this->url); | |
} | |
$meta = array( | |
'poster' => $this->url . "/share/posters/{$name}.png", | |
'title' => 'Я собрал этот отличный конструктор, собери и ты', | |
'description' => 'Тут более подробное описание', | |
); | |
extract( $meta ); | |
include_once __DIR__ . '/templates/page.php'; | |
} | |
/** | |
* Routing an incoming request. | |
* | |
* @param string $action Request control parameter. | |
*/ | |
private function route_request($action) { | |
if ('upload' === $action) { | |
return $this->upload_poster(); | |
} | |
$this->show_tags($action); | |
} | |
/** | |
* Start with server request uri. | |
* | |
* @param string $request Request URI. | |
*/ | |
public function init($request) { | |
$args = explode('/', trim($request, '/')); | |
if (empty($args[1])) { | |
$this->redirect_page($this->url); | |
} | |
$this->route_request($args[1]); | |
} | |
} | |
(new Sharing)->init($_SERVER['REQUEST_URI']); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="ru"> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="title" content="<?= $title; ?>"> | |
<meta name="description" content="<?= $description; ?>"> | |
<meta property="og:title" content="<?= $title; ?>"> | |
<meta property="og:description" content="<?= $description; ?>"> | |
<meta property="og:image" content="<?= $poster ?>"> | |
<meta name="twitter:card" content="summary_large_image"> | |
<meta name="twitter:image" content="<?= $poster ?>"> | |
<title><?php echo $title; ?></title> | |
</head> | |
<body> | |
<script type="text/javascript"> | |
window.location.href = '<?php echo $this->url; ?>'; | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment