先大家预览下效果
下载后的图片
这是怎么实现的呢,其实很简单,使用html2canvas 和 jquery
准备需要下载的盒子
我们需要用div拼出自己需要的保存区域,并为他设置一个ID
在下载按钮上绑定下载download函数
这是我的需要下载的div
import React, { useState, useEffect } from 'react';
import './SexChart.css';
import { Icon, Tooltip } from 'antd'
import { download } from './download'
import numeral from 'numeral'
console.log("download", download)
function SexChart() {
const data = {
"type": 0,
"month": "202002",
"cityName": "北京市",
"name": "蓝色港湾",
"rank": 54,
"male": 74926,
"female": 69975,
"age0018": 2352,
"age1924": 54,
"age2529": 20073,
"age3034": 33530,
"age3539": 24116,
"age4044": 14284,
"age4549": 11969,
"age5054": 8562,
"age5559": 6735,
"age6064": 3911,
"age6569": 2640,
"age70up": 1645,
"spi1": 54,
"spi2": 5632,
"spi3": 57345,
"spi4": 57253,
"spi5": 20453,
"spi6": 4013,
"spi7": 144,
"spi8": 7
}
const [boxWidth, setBoxWidth] = useState(0)
useEffect(() => {
setBoxWidth(document.getElementById('js-sex-box-id').clientWidth)
window.addEventListener('resize', resizeWindow)
return () => {
window.removeEventListener('resize', resizeWindow)
}
}, [])
function resizeWindow() {
setBoxWidth(document.getElementById('js-sex-box-id').clientWidth)
}
const ageKeys = Object.keys(data).filter(one => one.search('age') != -1)
const list = ageKeys.map((one, index) => {
const type = one.split('age')[1]
let start = type.slice(0, 2)
let end = type.slice(2, 4)
let typeName = `${start}-${end}`
if (start == '00') {
typeName = end + '以下'
}
if (end == 'up') {
typeName = start + '以上'
}
return {
male: data[one],
type: typeName
}
})
function getPercentage(number) {
const newArr = list.map((one) => one.female > one.male ? one.female : one.male).sort(function (a, b) {
return a - b;
})
const maxNumber = newArr[newArr.length - 1]
const size = (boxWidth - 160) / 6
const percentage = (number / maxNumber) * size
const count = percentage < 1 ? Math.ceil(percentage) : Math.round(percentage)
return count
}
function getNumber(number) {
let unit = ''
let numberFormat = number
if (number > 9999) {
numberFormat = numeral(numberFormat / 10000).format(',')
unit = '万'
}
return numberFormat + unit
}
function getPre(key) {
const count = data.male + data.female
return numeral(data[key] / count).format('0.0%')
}
return (
<div className='sex-chart' id='js-sex-box-id'>
<div className='sex-chart-title'>
年龄性别
<div className='download-box'>
<Icon type="download" className='download-icon' onClick={() => download('js-download-id')} />
<div className='save-img'>保存为图片</div>
</div>
</div>
<div id='js-download-id'>
<div className='sum-count'>
<div className='male-number'>{getNumber(data.male)}人</div>
<div className='male'>男性:{getPre('male')}</div>
{/* <div className='male-type'></div> */}
<div className='female'>女性:{getPre('female')}</div>
<div className='female-number'>{getNumber(data.female)}人</div>
</div>
<div>
{list.map((one, index) => <div key={index} className='row-sex-number'>
<div className='male-number number-width'>{getNumber(one.male)}</div>
<div className='male person-width'>
<Tooltip>
<PersonNumber number={getPercentage(one.male)} />
</Tooltip>
</div>
<div className='male-type'>{one.type}</div>
</div>)}
</div>
</div>
</div>
);
}
export default SexChart;
function PersonNumber({ number, sex = 'male' }) {
let arr = []
for (let index = 0; index < number; index++) {
arr.push(index)
}
return <PersonLine sex={sex} number={number}>
<div className='person-number'>{arr.map((one, index) => {
return sex == 'male' ?
<div className='blue-person' key={index}><SvgPerson fill={'#32C5FF'} /></div>
: <div className='pink-person' key={index + 'pink'}><SvgPerson /></div>
})}
</div></PersonLine>
}
function PersonLine(props) {
const { sex, number = 0 } = props
const isMale = sex == 'male'
const leftWidth = `calc(100% - ${number * 6 + 10}px)`
const left = (isMale ? '0' : `${number * 6 + 10}px`)
return <div className='person-line-box'>
<div className='person-line'
style={{ width: leftWidth, left: left, borderColor: isMale ? '' : '#E380AD' }}></div>
<div className={`content ${isMale ? 'right-person' : ''}`}
style={{ width: `${number * 6}px` }}
>{props.children}</div>
</div>
}
function SvgPerson({ fill = '#E380AD' }) {
return <svg>
<path d="M2.99986926,4 C3.86133561,4 4.62424822,4.55622026 4.88774211,5.37640026 L5.9536086,8.69413328 C6.12253412,9.21994941 5.83321712,9.78314881 5.30740098,9.95207434 C5.20855157,9.98383105 5.10535957,10 5.00153426,10 L3.96365973,10 L3.96365973,13 L2.03607879,13 L2.03607879,10 L0.998204263,10 C0.445919513,10 -0.00179573724,9.55228475 -0.00179573724,9 C-0.00179573724,8.89617469 0.0143732171,8.79298269 0.0461299237,8.69413328 L1.11199641,5.37640026 C1.3754903,4.55622026 2.13840291,4 2.99986926,4 Z M2.99986926,0 C3.93137112,0 4.68650258,0.783501688 4.68650258,1.75 C4.68650258,2.71649831 3.93137112,3.5 2.99986926,3.5 C2.0683674,3.5 1.31323594,2.71649831 1.31323594,1.75 C1.31323594,0.783501688 2.0683674,0 2.99986926,0 Z" stroke="none" fill={fill}></path>
</svg>
}
样式文件
.sex-chart { padding: 20px; width: 100%; font-size: 12px; color: #000}.sex-chart-title { display: flex; justify-content: space-between; margin-right: 10px; margin-bottom: 10px;}.download-icon { font-size: 16px;}.download-icon:hover { color: rgba(0, 131, 250, 0.85);}.download-box { width: 20px; height: 20px; position: relative; transition: all 0.3s;}.download-box:hover>.save-img { display: block;}.save-img { min-width: 100px; display: none; position: fixed; margin-left: -20px; margin-top: -4px; color: rgba(0, 131, 250, 0.85);}.blue-person,.pink-person { width: 6px; height: 13px; padding: 1px;}.person-number { display: flex;}.sum-count { justify-content: space-between;}.row-sex-number,.sum-count { display: flex; width: 100%;}.male-number,.blue-person { color: #32C5FF;}.female-number,.pink-person { color: #E380AD;}.female-number { text-align: right;}.male-type { width: 80px; text-align: center;}.number-width { width: 50px;}.person-width { width: calc((100% - 130px));}.person-line-box { position: relative;}.person-line { height: 1px; border-top: 1px dashed #32C5FF; position: absolute; top: 13px; z-index: -1;}.right-person { right: 0px;}.content { position: absolute; top: 4px;}
download函数
import html2canvas from "html2canvas";
import $ from "jquery";
const { atob, URL, Uint8Array, Blob, navigator } = window;
var dataURIToBlob = function (imgName, dataURI, callback) {
var binStr = atob(dataURI.split(',')[1]),
len = binStr.length,
arr = new Uint8Array(len);
for (var i = 0; i < len; i++) {
arr[i] = binStr.charCodeAt(i);
}
callback(imgName, new Blob([arr]));
}
var callback = function (imgName, blob) {
var triggerDownload = $("<a>").attr("href", URL.createObjectURL(blob)).attr("download", imgName).appendTo("body").on("click", function () {
if (navigator.msSaveBlob) {
return navigator.msSaveBlob(blob, imgName);
}
});
triggerDownload[0].click();
triggerDownload.remove();
};
export const download = (id, name = '年龄性别') => {
// canvas生成图片
var getPixelRatio = function (context) { // 获取设备的PixelRatio
var backingStore = context.backingStorePixelRatio ||
context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;
return (window.devicePixelRatio || 1) / backingStore;
};
//生成的图片名称
var imgName = `${name}.jpg`;
var shareContent = document.getElementById(id);
var width = shareContent.offsetWidth + 50;
var height = shareContent.offsetHeight + 50;
var canvas = document.createElement("canvas");
var context = canvas.getContext('2d');
var scale = getPixelRatio(context); //将canvas的容器扩大PixelRatio倍,再将画布缩放,将图像放大PixelRatio倍。
canvas.width = width * scale;
canvas.height = height * scale;
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
context.scale(scale, scale);
var opts = {
scale: scale,
canvas: canvas,
width: width,
height: height,
dpi: window.devicePixelRatio
};
html2canvas(shareContent, opts).then(function (canvas) {
context.imageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.msImageSmoothingEnabled = false;
context.imageSmoothingEnabled = false;
var dataUrl = canvas.toDataURL('image/jpeg', 1.0);
dataURIToBlob(imgName, dataUrl, callback);
});
}