在react里div画的图表能下载?进来看看👀

270 阅读3分钟

先大家预览下效果

下载后的图片

这是怎么实现的呢,其实很简单,使用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);
    });
}

想了解更多组件,或者有组件需求可以关注我