Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
H
html-shot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
劳工
html-shot
Commits
71a41d1f
Commit
71a41d1f
authored
Jan 12, 2021
by
wjf
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
toCanvas
parent
dfa7baea
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
433 additions
and
1 deletion
+433
-1
ToCanvas.js
dist/ToCanvas.js
+143
-0
dom-parser.js
dist/dom-parser.js
+85
-0
index.js
dist/index.js
+18
-0
index.ts
src/index.ts
+15
-1
toCanvas.ts
src/toCanvas.ts
+172
-0
No files found.
dist/ToCanvas.js
0 → 100644
View file @
71a41d1f
/**
* 节点类型
*/
var
NodeType
;
(
function
(
NodeType
)
{
NodeType
[
NodeType
[
"TEXT"
]
=
0
]
=
"TEXT"
;
NodeType
[
NodeType
[
"IMAGE"
]
=
1
]
=
"IMAGE"
;
})(
NodeType
||
(
NodeType
=
{}));
/**
*
* @param data
* @param callback
*/
export
async
function
toCanvas
(
data
,
callback
)
{
const
{
nodes
,
width
,
height
}
=
data
;
var
canvas
=
document
.
createElement
(
"canvas"
);
canvas
.
width
=
width
;
canvas
.
height
=
height
;
var
ctx
=
canvas
.
getContext
(
"2d"
);
//先加载完所有图片
var
p
=
[];
nodes
.
forEach
((
n
)
=>
{
if
(
n
.
type
!==
NodeType
.
IMAGE
)
return
;
p
.
push
(
new
Promise
((
r
)
=>
{
let
img
=
new
Image
();
img
.
onload
=
()
=>
{
n
.
img
=
img
;
r
();
};
img
.
src
=
n
.
src
;
}));
});
if
(
p
.
length
)
await
Promise
.
all
(
p
);
nodes
.
forEach
((
n
)
=>
{
switch
(
n
.
type
)
{
case
NodeType
.
IMAGE
:
drawImage
(
n
,
ctx
);
break
;
case
NodeType
.
TEXT
:
drawText
(
n
,
ctx
);
break
;
}
});
callback
&&
callback
(
canvas
);
return
canvas
;
}
/**
* 绘制图片
* @param data
* @param ctx
*/
function
drawImage
(
data
,
ctx
)
{
const
{
x
,
y
,
img
,
width
,
height
,
borderRadius
}
=
data
;
if
(
borderRadius
)
{
//有圆角,画遮罩,暂时只管一个
ctx
.
save
();
let
max
=
(
width
<
height
?
width
:
height
)
/
2
;
let
radius
=
Math
.
min
(
+
borderRadius
.
replace
(
"px"
,
""
),
max
);
// ctx.beginPath();
// ctx.moveTo(x, y + radius);
// ctx.quadraticCurveTo(x, y, x + radius, y);
// ctx.lineTo(x + width - radius, y);
// ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
// ctx.lineTo(x + width, y + height - radius);
// ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
// ctx.lineTo(x + radius, y + height);
// ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
// ctx.lineTo(x, y + radius);
// ctx.clip();
ctx
.
beginPath
();
ctx
.
moveTo
(
x
+
width
-
radius
,
y
);
ctx
.
arcTo
(
x
+
width
,
y
,
x
+
width
,
y
+
radius
,
radius
);
ctx
.
lineTo
(
x
+
width
,
y
+
height
-
radius
);
ctx
.
arcTo
(
x
+
width
,
y
+
height
,
x
+
width
-
radius
,
y
+
height
,
radius
);
ctx
.
lineTo
(
x
+
radius
,
y
+
height
);
ctx
.
arcTo
(
x
,
y
+
height
,
x
,
y
+
height
-
radius
,
radius
);
ctx
.
lineTo
(
x
,
y
+
radius
);
ctx
.
arcTo
(
x
,
y
,
x
+
radius
,
y
,
radius
);
ctx
.
closePath
();
ctx
.
clip
();
}
ctx
.
drawImage
(
img
,
x
,
y
,
width
,
height
);
if
(
borderRadius
)
ctx
.
restore
();
}
/**
* 绘制文本,暂时没那么复杂,不用离屏玩
* @param data
* @param ctx
*/
function
drawText
(
data
,
ctx
)
{
const
{
x
,
y
,
width
,
height
,
text
,
color
,
fontSize
,
fontWeight
,
wordWrap
,
textAlign
}
=
data
;
var
font
=
fontSize
;
font
+=
" Arial"
;
//字体没有
//font-weight:bold;font-style:italic;
if
(
fontWeight
)
font
=
fontWeight
+
" "
+
font
;
ctx
.
font
=
font
;
ctx
.
textBaseline
=
"top"
;
ctx
.
fillStyle
=
color
;
var
widthAll
=
ctx
.
measureText
(
text
).
width
;
// console.log(ctx.measureText(text))
//超过宽度需要换行,且需要用到居中方式
if
(
wordWrap
==
"break-word"
&&
widthAll
>
width
)
{
let
realLines
=
[];
let
w
=
ctx
.
measureText
(
text
[
0
]).
width
;
let
lineStr
=
text
[
0
];
let
wordW
=
0
;
let
strLen
=
text
.
length
;
for
(
let
j
=
1
;
j
<
strLen
;
j
++
)
{
wordW
=
ctx
.
measureText
(
text
[
j
]).
width
;
w
+=
wordW
;
if
(
w
>
width
)
{
realLines
[
realLines
.
length
]
=
lineStr
;
lineStr
=
text
[
j
];
w
=
wordW
;
}
else
{
lineStr
+=
text
[
j
];
}
}
//最后一行
realLines
[
realLines
.
length
]
=
lineStr
;
ctx
.
textAlign
=
textAlign
||
"left"
;
let
tx
=
0
;
if
(
ctx
.
textAlign
==
"center"
)
{
tx
=
width
*
0.5
;
}
else
if
(
ctx
.
textAlign
==
"right"
)
{
tx
=
width
;
}
//有待考虑.现在直接拿高度取平均算每行高度
var
lineH
=
height
/
realLines
.
length
;
realLines
.
forEach
((
r
,
i
)
=>
{
ctx
.
fillText
(
r
,
x
+
tx
,
y
+
i
*
lineH
);
});
}
else
{
ctx
.
textAlign
=
"left"
;
ctx
.
fillText
(
text
,
x
,
y
);
}
}
dist/dom-parser.js
0 → 100644
View file @
71a41d1f
/**
* Created by rockyl on 2021/1/11.
*/
const
styleKeys
=
[
'fontSize'
,
'fontFamily'
,
'fontWeight'
,
'textAlign'
,
'color'
,
'wordWrap'
,
'borderRadius'
,
];
export
function
parseDom
(
el
)
{
const
{
left
:
pX
,
top
:
pY
,
width
,
height
}
=
el
.
getBoundingClientRect
();
let
nodes
=
[];
walkNode
(
el
,
function
(
childNode
)
{
let
vNode
,
bound
,
node
;
if
(
childNode
.
nodeName
===
'IMG'
)
{
//其他全部认为是文本
node
=
childNode
;
vNode
=
{
type
:
1
,
src
:
childNode
.
src
,
};
bound
=
childNode
.
getBoundingClientRect
();
}
else
{
if
(
childNode
.
data
)
{
node
=
childNode
.
parentElement
;
let
text
=
childNode
.
data
.
trim
();
if
(
text
)
{
//过滤空文本
let
range
=
document
.
createRange
();
range
.
selectNode
(
childNode
);
bound
=
range
.
getBoundingClientRect
();
range
.
detach
();
vNode
=
{
type
:
0
,
text
,
};
}
}
}
if
(
vNode
)
{
const
{
left
,
top
,
width
,
height
}
=
bound
;
vNode
.
x
=
left
-
pX
;
vNode
.
y
=
top
-
pY
;
vNode
.
width
=
width
;
vNode
.
height
=
height
;
let
styles
=
getStylesWithoutDefaults
(
node
);
for
(
let
skey
in
styles
)
{
if
(
styleKeys
.
indexOf
(
skey
)
<
0
)
{
continue
;
}
vNode
[
skey
]
=
styles
[
skey
];
}
nodes
.
push
(
vNode
);
}
});
return
{
width
,
height
,
nodes
,
};
}
function
walkNode
(
root
,
callback
)
{
callback
(
root
);
for
(
let
i
=
0
,
li
=
root
.
childNodes
.
length
;
i
<
li
;
i
++
)
{
const
childNode
=
root
.
childNodes
[
i
];
walkNode
(
childNode
,
callback
);
}
}
function
getStylesWithoutDefaults
(
element
)
{
let
dummy
=
document
.
createElement
(
'element-'
+
(
new
Date
().
getTime
()));
document
.
body
.
appendChild
(
dummy
);
let
defaultStyles
=
getComputedStyle
(
dummy
);
let
elementStyles
=
getComputedStyle
(
element
);
let
diff
=
{};
for
(
let
key
in
elementStyles
)
{
if
(
elementStyles
.
hasOwnProperty
(
key
)
&&
defaultStyles
[
key
]
!==
elementStyles
[
key
])
{
diff
[
key
]
=
elementStyles
[
key
];
}
}
dummy
.
remove
();
return
diff
;
}
dist/index.js
0 → 100644
View file @
71a41d1f
/**
* Created by rockyl on 2021/1/11.
*/
import
{
parseDom
}
from
"./dom-parser.js"
;
import
{
toCanvas
}
from
"./toCanvas.js"
;
let
data
=
parseDom
(
document
.
getElementById
(
'poster'
));
console
.
log
(
data
);
toCanvas
(
data
,
(
c
)
=>
{
document
.
body
.
appendChild
(
c
);
});
/**
*
* @param el html文本
* @param callback
*/
export
function
htmlToCanvas
(
el
,
callback
)
{
return
toCanvas
(
parseDom
(
el
),
callback
);
}
src/index.ts
View file @
71a41d1f
...
...
@@ -2,7 +2,21 @@
* Created by rockyl on 2021/1/11.
*/
import
{
parseDom
}
from
"./dom-parser.js"
;
import
{
parseDom
}
from
"./dom-parser.js"
;
import
{
toCanvas
}
from
"./toCanvas.js"
;
let
data
=
parseDom
(
document
.
getElementById
(
'poster'
));
console
.
log
(
data
);
toCanvas
(
data
,(
c
)
=>
{
document
.
body
.
appendChild
(
c
)
})
/**
*
* @param el html文本
* @param callback
*/
export
function
htmlToCanvas
(
el
:
HTMLElement
,
callback
?:(
HTMLCanvasElement
)
=>
void
):
Promise
<
HTMLCanvasElement
>
{
return
toCanvas
(
parseDom
(
el
),
callback
)
}
\ No newline at end of file
src/toCanvas.ts
0 → 100644
View file @
71a41d1f
/**
* 转换接口数据
*/
interface
ICData
{
width
:
number
,
height
:
number
,
nodes
:
INodeData
[]
}
/**
* 节点类型
*/
enum
NodeType
{
TEXT
=
0
,
IMAGE
}
/**
* 节点数据
*/
interface
INodeData
{
type
:
NodeType
,
x
:
number
,
y
:
number
,
width
:
number
,
height
:
number
,
//图片需要
src
?:
string
,
borderRadius
?:
string
,
img
?:
HTMLImageElement
,
//加载后挂上的
//文本需要
text
?:
string
color
?:
string
,
fontSize
?:
string
,
//文本字号
fontWeight
?:
string
,
//文本粗细
wordWrap
?:
"break-word"
|
null
textAlign
?:
"center"
|
"left"
|
"right"
}
/**
*
* @param data
* @param callback
*/
export
async
function
toCanvas
(
data
:
ICData
,
callback
?:
(
canvas
:
HTMLCanvasElement
)
=>
void
):
Promise
<
HTMLCanvasElement
>
{
const
{
nodes
,
width
,
height
}
=
data
;
var
canvas
=
document
.
createElement
(
"canvas"
);
canvas
.
width
=
width
;
canvas
.
height
=
height
;
var
ctx
=
canvas
.
getContext
(
"2d"
);
//先加载完所有图片
var
p
:
Promise
<
void
>
[]
=
[];
nodes
.
forEach
((
n
)
=>
{
if
(
n
.
type
!==
NodeType
.
IMAGE
)
return
;
p
.
push
(
new
Promise
((
r
)
=>
{
let
img
=
new
Image
();
img
.
onload
=
()
=>
{
n
.
img
=
img
;
r
()
}
img
.
src
=
n
.
src
;
})
)
})
if
(
p
.
length
)
await
Promise
.
all
(
p
);
nodes
.
forEach
((
n
)
=>
{
switch
(
n
.
type
)
{
case
NodeType
.
IMAGE
:
drawImage
(
n
,
ctx
)
break
case
NodeType
.
TEXT
:
drawText
(
n
,
ctx
)
break
}
})
callback
&&
callback
(
canvas
)
return
canvas
}
/**
* 绘制图片
* @param data
* @param ctx
*/
function
drawImage
(
data
:
INodeData
,
ctx
:
CanvasRenderingContext2D
)
{
const
{
x
,
y
,
img
,
width
,
height
,
borderRadius
}
=
data
;
if
(
borderRadius
)
{
//有圆角,画遮罩,暂时只管一个
ctx
.
save
();
let
max
=
(
width
<
height
?
width
:
height
)
/
2
;
let
radius
=
Math
.
min
(
+
borderRadius
.
replace
(
"px"
,
""
),
max
);
// ctx.beginPath();
// ctx.moveTo(x, y + radius);
// ctx.quadraticCurveTo(x, y, x + radius, y);
// ctx.lineTo(x + width - radius, y);
// ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
// ctx.lineTo(x + width, y + height - radius);
// ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
// ctx.lineTo(x + radius, y + height);
// ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
// ctx.lineTo(x, y + radius);
// ctx.clip();
ctx
.
beginPath
();
ctx
.
moveTo
(
x
+
width
-
radius
,
y
);
ctx
.
arcTo
(
x
+
width
,
y
,
x
+
width
,
y
+
radius
,
radius
)
ctx
.
lineTo
(
x
+
width
,
y
+
height
-
radius
)
ctx
.
arcTo
(
x
+
width
,
y
+
height
,
x
+
width
-
radius
,
y
+
height
,
radius
)
ctx
.
lineTo
(
x
+
radius
,
y
+
height
)
ctx
.
arcTo
(
x
,
y
+
height
,
x
,
y
+
height
-
radius
,
radius
)
ctx
.
lineTo
(
x
,
y
+
radius
)
ctx
.
arcTo
(
x
,
y
,
x
+
radius
,
y
,
radius
)
ctx
.
closePath
();
ctx
.
clip
();
}
ctx
.
drawImage
(
img
,
x
,
y
,
width
,
height
)
if
(
borderRadius
)
ctx
.
restore
();
}
/**
* 绘制文本,暂时没那么复杂,不用离屏玩
* @param data
* @param ctx
*/
function
drawText
(
data
:
INodeData
,
ctx
:
CanvasRenderingContext2D
)
{
const
{
x
,
y
,
width
,
height
,
text
,
color
,
fontSize
,
fontWeight
,
wordWrap
,
textAlign
}
=
data
;
var
font
=
fontSize
;
font
+=
" Arial"
;
//字体没有
//font-weight:bold;font-style:italic;
if
(
fontWeight
)
font
=
fontWeight
+
" "
+
font
;
ctx
.
font
=
font
;
ctx
.
textBaseline
=
"top"
;
ctx
.
fillStyle
=
color
;
var
widthAll
=
ctx
.
measureText
(
text
).
width
;
// console.log(ctx.measureText(text))
//超过宽度需要换行,且需要用到居中方式
if
(
wordWrap
==
"break-word"
&&
widthAll
>
width
)
{
let
realLines
=
[];
let
w
=
ctx
.
measureText
(
text
[
0
]).
width
;
let
lineStr
=
text
[
0
];
let
wordW
=
0
;
let
strLen
=
text
.
length
;
for
(
let
j
=
1
;
j
<
strLen
;
j
++
)
{
wordW
=
ctx
.
measureText
(
text
[
j
]).
width
;
w
+=
wordW
;
if
(
w
>
width
)
{
realLines
[
realLines
.
length
]
=
lineStr
;
lineStr
=
text
[
j
];
w
=
wordW
;
}
else
{
lineStr
+=
text
[
j
];
}
}
//最后一行
realLines
[
realLines
.
length
]
=
lineStr
;
ctx
.
textAlign
=
textAlign
||
"left"
;
let
tx
=
0
;
if
(
ctx
.
textAlign
==
"center"
)
{
tx
=
width
*
0.5
;
}
else
if
(
ctx
.
textAlign
==
"right"
)
{
tx
=
width
;
}
//有待考虑.现在直接拿高度取平均算每行高度
var
lineH
=
height
/
realLines
.
length
;
realLines
.
forEach
((
r
,
i
)
=>
{
ctx
.
fillText
(
r
,
x
+
tx
,
y
+
i
*
lineH
)
})
}
else
{
ctx
.
textAlign
=
"left"
ctx
.
fillText
(
text
,
x
,
y
)
}
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment