【HTML+js】微信微博九宫格图拆分器
本帖最后由 南郊居士 于 2020-2-17 22:44 编辑最后一次更新本帖,以后对本工具的任何修改都不再另行发布了:
2020.2.17
1.新增了心形裁剪功能
为制作这个功能,绘制心形线时遇到困难,因为找不到现成的工具,所以又另做了一个新工具:三次贝塞尔曲线坐标手工拟合并生成,链接:https://www.52pojie.cn/thread-1108704-1-1.html
---------------------------------- 正文分割线 ----------------------------------------
网上很多教程在教如何制做微博微信九宫格图片(据说安卓微博自带这个功能,但是果机不行),有用PS的,有需要安装App的,有用微信小程序的,不一而足。
其实这是个非常简单的小功能,写个前端页就可以轻松搞定,你只需要一张美美的照片和一个支持H5的浏览器就够了。
名称:微信四九城(随便乱起的名字,因为可以生成4张或9张图)
功能:
1、图片的九宫格或四分格拆分并保存到本地,测试Chrome 80.0.3987.106、EMUI10.0系统浏览器和iOS 12.3.1 Safari均可正常使用。
2、图片旋转、图片裁剪。
3、支持图片的圆角矩形和圆形裁剪、裁剪掉的部分可选保留透明通道。
4、jpeg格式可以选择图片质量。
效果:
Safari不支持链接存图,只能长按保存,并不影响图片质量。
测试链接:http://gxsoftware.gitee.io/imageswall
只有一个html文件,源码自取。
界面自带劝退功能,和那些美美哒可爱多的微信小程序没有可比性,颜控退散{:1_911:} 南郊居士 发表于 2020-2-18 22:38
你可能被web劫持了,我的源码里没有这一段。
检查一下你的路由器吧。
小弟无才,复下来是这样的,运行不了
<!DOCTYPE html>
<!-- saved from url=(0038)http://gxsoftware.gitee.io/imageswall/ -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width = device-width, height = device-height, maximum-scale = 1.0, user-scalable = no">
<style type="text/css">
.wx_button {
border-spacing:0px;
border-style: solid;
border-color: #38f;
border-width: 1px;
display: inline-block;
line-height: 2em;
text-align: center;
vertical-align: middle;
background-color: #f8f8f8;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.wx_image {
width: 73px;
height: 73px;
margin: 1px;
border:1px solid #000;
}
.wx_list {
margin: 0;
padding: 0;
font-size: 0;
}
.wx_doing {
position: fixed;
top: 40%;
height: 4em;
left: 20%;
right: 20%;
background-color: #FFF;
border: 3px solid #000;
display: inline-block;
line-height: 4em;
vertical-align: middle;
text-align: center;
}
</style>
<title>微信四九城</title>
</head>
<body>
<h3>微信四九城</h3>
<div style="margin: 1em 0">用于微信四分格和九分格图片的分割生成<br>纯前端,保护您的隐私</div>
<div>
<input id="upload_input" type="file" accept="image/*" style="display: none;">
<label for="upload_input"><div class="wx_button" style="width: 8em;">点击选择图片</div></label>
<input id="nine" type="radio" name="count" checked="checked"><label for="nine">九格</label>
<input id="four" type="radio" name="count"><label for="four">四格</label>
</div>
<div id="make" style="margin: 1em 0; display: none;">
<canvas id="canvas" style="margin: 0 0 1em 0; border: 1px solid #000;">不支持本功能</canvas><br>
<div style="margin: 0 0 1em 0;">放大:<input type="range" id="enlarge" min="1" max="3" step="0.01" value="0" style="width: 15em;"></div>
<div id="qua" style="margin: 0 0 1em 0;">质量:<input type="range" id="quality" min="0" max="100" step="1" value="75" style="width: 15em;"></div>
<div style="margin: 0 0 1em 0;"><input id="alpha" type="checkbox"><label for="alpha">保留透明通道</label></div>
<div style="margin: 0 0 1em 0;">
<input id="normal" type="radio" name="style" checked="checked"><label for="normal">不裁剪</label>
<input id="roundrect" type="radio" name="style"><label for="roundrect">圆角裁剪</label>
<input id="round" type="radio" name="style"><label for="round">圆形裁剪</label>
<input id="heart" type="radio" name="style"><label for="heart">心形裁剪</label>
</div>
<div class="wx_button" style="width: 8em; margin: 0 8px 0 0;">旋转90°</div><div class="wx_button" style="width: 8em;">生成</div>
</div>
<div id="result" style="margin: 1em 0; display: none;"></div>
<div id="doing" class="wx_doing" style="display: none;">处理中,请勿操作</div>
<hr>
<small>GX Software 2020 by 南郊居士</small>
<script type="text/javascript">
var touchdown = false, preclick;
var l, t;
var img;
var doing = false;
CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r)
{
if (w < 2 * r) {
r = w / 2;
}
if (h < 2 * r) {
r = h / 2;
}
this.beginPath();
this.moveTo(x + r, y);
this.arcTo(x + w, y, x + w, y + h, r);
this.arcTo(x + w, y + h, x, y + h, r);
this.arcTo(x, y + h, x, y, r);
this.arcTo(x, y, x + w, y, r);
this.closePath();
return this;
}
CanvasRenderingContext2D.prototype.heart = function (x, y, r)
{
this.translate(x, y);
this.beginPath();
this.moveTo(-0.004082 * r, -0.683418 * r);
this.bezierCurveTo(-0.15102 * r, -1.030357 * r, -0.591837 * r, -1.11199 * r, -0.865306 * r, -0.793622 * r);
this.bezierCurveTo(-1.036735 * r, -0.577296 * r, -1.02449 * r, -0.275255 * r, -0.914286 * r, -0.014031 * r);
this.bezierCurveTo(-0.738776 * r, 0.369643 * r, -0.338776 * r, 0.581888 * r, -0.004082 * r, 0.998214 * r);
this.bezierCurveTo(0.17551 * r, 0.745153 * r, 0.693878 * r, 0.385969 * r, 0.84898 * r, 0.096173 * r);
this.bezierCurveTo(1.044898 * r, -0.173214 * r, 1.053061 * r, -0.66301 * r, 0.812245 * r, -0.846684 * r);
this.bezierCurveTo(0.538776 * r, -1.107908 * r, 0.183673 * r, -1.026276 * r, -0.004082 * r, -0.683418 * r);
this.closePath();
this.translate(-x, -y);
return this;
}
function isSafari()
{
return (/Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent));
}
function loadPic(e)
{
if (doing) {
return;
}
var selectedFile = document.getElementById("upload_input").files;
readFileData(selectedFile, function(image) {
const test = document.getElementById("upload_input");
test.outerHTML = test.outerHTML;
const count = document.getElementById("nine").checked ? 9 : 4;
l = t = 0;
document.getElementById("enlarge").value = 0;
const can = document.getElementById("canvas");
try {
returnToEdit(false);
can.width = can.height = Math.min(window.innerWidth * 0.95, window.innerHeight * 0.75);
if("ontouchstart" in window) {
can.addEventListener("touchstart", down, false);
can.addEventListener("touchmove", move, false);
window.addEventListener("touchend", up, false);
}
else {
can.addEventListener("mousedown", down, false);
can.addEventListener("mousemove", move, false);
window.addEventListener('mouseup', up, false);
}
draw();
window.scrollTo(0, can.offsetTop);
}
catch(e) {}
});
}
function readFileData(file, callback)
{
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(e) {
img = new Image();
img.src = e.target.result;
img.onload = function() {
var s = img.src.match(/data:.*;/).match(/image.*;/).match(/.*[^;]/);
document.getElementById("qua").style.display = (s == "image/jpeg" || s == "image/webp") ? "" : "none";
callback(img);
}
}
}
function draw()
{
if (!img) {
return;
}
const count = document.getElementById("nine").checked ? 9 : 4;
const len = Math.min(img.width, img.height);
const can = document.getElementById("canvas");
const ctx = can.getContext("2d");
const width = can.width;
const e = document.getElementById("enlarge").value;
if (l + len / e > img.width) {
l = img.width - len / e;
}
else if (l < 0) {
l = 0;
}
if (t + len / e > img.height) {
t = img.height - len / e;
}
else if (t < 0) {
t = 0;
}
ctx.clearRect(0, 0, can.width, can.height);
ctx.save();
switch(getRadioValue("style"))
{
case 1:
ctx.roundRect(0, 0, width, width, width / Math.sqrt(count) / 2);
ctx.clip();
break;
case 2:
ctx.beginPath();
ctx.arc(width / 2, width / 2, width / 2, 0, 2 * Math.PI);
ctx.clip();
break;
case 3:
ctx.heart(width / 2, width / 2, width / 2);
ctx.clip();
break;
}
ctx.drawImage(img, l, t, len, len, 0, 0, width * e, width * e);
if (count == 9) {
const cw = width / 892 * 8;
ctx.clearRect(width / 3 - cw / 2, 0, cw, width);
ctx.clearRect(width / 3 * 2 - cw / 2, 0, cw, width);
ctx.clearRect(0, width / 3 - cw / 2, width, cw);
ctx.clearRect(0, width / 3 * 2 - cw / 2, width, cw);
}
else {
const cw = width / 592 * 8;
ctx.clearRect(width / 2 - cw / 2, 0, cw, width);
ctx.clearRect(0, width / 2 - cw / 2, width, cw);
}
ctx.restore();
}
function getPos(c, e)
{
var box = c.getBoundingClientRect();
var x1, y1;
if ("ontouchstart" in window) {
if (e.targetTouches.length) {
x1 = e.targetTouches.clientX;
y1 = e.targetTouches.clientY;
}
else {
x1 = e.changedTouches.clientX;
y1 = e.changedTouches.clientY;
touchdown = false;
}
}
else {
x1 = e.clientX;
y1 = e.clientY;
}
return {
x: (x1 - box.left) * (c.width / box.width),
y: (y1 - box.top) * (c.height / box.height)
};
}
function prevent(e)
{
if(window.event) {
window.event.returnValue = false;
}
else {
e.preventDefault();
}
}
function down(e)
{
if (doing) {
return;
}
prevent(e);
const p = getPos(document.getElementById("canvas"), e);
if ("ontouchstart" in window) {
if (!touchdown) {
touchdown = true;
}
else {
return;
}
}
preclick = p;
}
function up(e)
{
getPos(document.getElementById("canvas"), e);
if (touchdown) {
return;
}
preclick = null;
}
function move(e)
{
if (!preclick) {
return;
}
const en = document.getElementById("enlarge").value;
const p = getPos(document.getElementById("canvas"), e);
const len = Math.min(img.width, img.height);
const can = document.getElementById("canvas");
l -= (p.x - preclick.x) / can.width * len / en;
t -= (p.y - preclick.y) / can.height * len / en;
draw();
preclick.x = p.x;
preclick.y = p.y;
}
function make()
{
setDoing(true);
try {
const e = document.getElementById("enlarge").value;
const len = Math.min(img.width, img.height);
const can = document.createElement("canvas");
can.width = can.height = len;
const ctx = can.getContext("2d");
var s = img.src.match(/data:.*;/).match(/image.*;/).match(/.*[^;]/);
if (document.getElementById("alpha").checked && getRadioValue("style") != 0) {
s = "image/png";
ctx.clearRect(0, 0, can.width, can.height);
}
else if (!document.getElementById("alpha").checked) {
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, len, len);
}
switch(getRadioValue("style"))
{
case 1:
ctx.roundRect(0, 0, len, len, len / Math.sqrt(document.getElementById("nine").checked ? 9 : 4) / 2);
ctx.clip();
break;
case 2:
ctx.beginPath();
ctx.arc(len / 2, len / 2, len / 2, 0, 2 * Math.PI);
ctx.clip();
break;
case 3:
ctx.heart(len / 2, len / 2, len / 2);
ctx.clip();
break;
}
ctx.drawImage(img, l, t, len, len, 0, 0, len * e, len * e);
var i = new Image();
i.src = can.toDataURL(s, 1);
i.onload = function() {
const len = this.width;
const count = document.getElementById("nine").checked ? 9 : 4;
const can = document.createElement("canvas");
const ctx = can.getContext("2d");
const quality = document.getElementById("quality");
let inner = "";
if (isSafari()) {
inner += "长按图片保存";
}
else {
inner += "点击图片保存";
}
var time = parseInt((new Date()).getTime() / 1000);
var s = this.src.match(/data:.*;/).match(/image.*;/).match(/.*[^;]/);
if (count == 9) {
const size = can.width = can.height = len / 892 * 292;
for (let i = 0, x = 0, y = 0, index = 1; i < 3; i++) {
inner += '<div class="wx_list">';
for (let j = 0; j < 3; j++, index++) {
ctx.clearRect(0, 0, can.width, can.height);
ctx.drawImage(this, x, y, size, size, 0, 0, can.width, can.height);
let url = can.toDataURL(s, quality.value / 100);
inner += single(url, time, index);
x += size + len / 892 * 8;
}
inner += "</div>";
x = 0;
y += size + len / 892 * 8;
}
}
else {
const size = can.width = can.height = len / 592 * 292;
for (let i = 0, x = 0, y = 0, index = 1; i < 2; i++) {
inner += '<div class="wx_list">';
for (let j = 0; j < 2; j++, index++) {
ctx.clearRect(0, 0, can.width, can.height);
ctx.drawImage(this, x, y, size, size, 0, 0, can.width, can.height);
let url = can.toDataURL(s, quality.value / 100);
inner += single(url, time, index);
x += size + len / 592 * 8;
}
inner += "</div>";
x = 0;
y += size + len / 592 * 8;
}
}
inner += '<div class = "wx_button" style = "width: 8em; margin: 8px 0 0 0;">返回编辑</div>';
const r = document.getElementById("result");
r.innerHTML = inner;
r.style.display = "";
document.getElementById("make").style.display = "none";
setDoing(false);
};
}
catch(e) {
alert("转存失败!");
setDoing(false);
}
}
function returnToEdit(scroll)
{
document.getElementById("make").style.display = "";
document.getElementById("result").innerHTML = "";
document.getElementById("result").style.display = "none";
if (scroll) {
window.scrollTo(0, document.getElementById("canvas").offsetTop);
}
}
function rotate()
{
setDoing(true);
try {
const can = document.createElement("canvas");
can.width = img.height;
can.height = img.width;
const ctx = can.getContext("2d");
ctx.translate(can.width / 2, can.height / 2);
ctx.rotate(Math.PI / 2);
ctx.drawImage(img, 0, 0, img.width, img.height, -img.width / 2, -img.height / 2, img.width, img.height);
var i = new Image();
var s = img.src.match(/data:.*;/).match(/image.*;/).match(/.*[^;]/);
i.src = can.toDataURL(s, 1);
i.onload = function() {
setDoing(false);
img = this;
l = t = 0;
draw();
}
}
catch(e) {
alert("旋转失败!");
setDoing(false);
}
}
function setDoing(d)
{
doing = d;
document.getElementById("nine").disabled = doing;
document.getElementById("four").disabled = doing;
document.getElementById("doing").style.display = doing ? "" : "none";
}
function getRadioValue(name)
{
var li = document.getElementsByName(name);
for (var i = 0; i < li.length; i++) {
if (li.checked) {
return i;
}
}
return 0;
}
function single(url, time, index)
{
if (isSafari()) {
return `<img src=${url} class="wx_image"/>`;
}
else {
return `<a download=${time}-${index} href=${url}><img src=${url} class="wx_image"/></a>`;
}
}
</script>
</body></html> longjingo 发表于 2020-3-23 16:57
亲测透明通道不可用 显示黑色区域
可能和你的系统有关系,有的系统在应用中会将透明通道显示为黑色(举例,iOS的桌面图标要求必须有底色,透明通道将显示为黑色),这个时候就需要保留白底色,用于在某些系统中和背景色保持一致,制造透明通道的“假象”。
如果你在Windows系统下用H5浏览器执行本工具,分割后另存为到本地用图片查看器查看,是可以看到透明通道的。 微信微博九宫格图拆,{:1_921:}{:1_921:} 很好 厉害 收藏先 感谢楼主分享。。 顶起,非常感谢楼主 js高手,已保存 谢谢楼主,辛苦了 感谢楼主,真的很好用! 收藏了,谢谢分享