react中使用截图组件Cropper组件
--最近项目用react,学习react并使用cropper组件裁剪图片。
(这里开发组件不够统一有用tsx(TypeScript + xml/html)写的组件,有用jsx(javascript+xml/html)写的组件
前言:cropper组件引入到项目中的手顺直接看官方文档;github:https://github.com/fengyuanchen/cropperjs#methods 在线演示url: https://fengyuanchen.github.io/cropper/
1.cropper组件以及各种操作的简单封装。
react-cropper.js文件
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Cropper from 'cropperjs';
const optionProps = [
'dragMode',
'aspectRatio',
'data',
'crop',
// unchangeable props start from here
'viewMode',
'preview',
'responsive',
'restore',
'checkCrossOrigin',
'checkOrientation',
'modal',
'guides',
'center',
'highlight',
'background',
'autoCrop',
'autoCropArea',
'movable',
'rotatable',
'scalable',
'zoomable',
'zoomOnTouch',
'zoomOnWheel',
'wheelZoomRatio',
'cropBoxMovable',
'cropBoxResizable',
'toggleDragModeOnDblclick',
'minContainerWidth',
'minContainerHeight',
'minCanvasWidth',
'minCanvasHeight',
'minCropBoxWidth',
'minCropBoxHeight',
'ready',
'cropstart',
'cropmove',
'cropend',
'zoom',
];
const unchangeableProps = optionProps;
class ReactCropper extends Component {
componentDidMount() {
const options = Object.keys(this.props)
.filter(propKey => optionProps.indexOf(propKey) !== -1)
.reduce((prevOptions, propKey) =>
Object.assign({}, prevOptions, { [propKey]: this.props[propKey] }), {});
this.cropper = new Cropper(this.img, options);
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.src !== this.props.src) {
this.cropper.reset().clear().replace(nextProps.src);
}
if (nextProps.aspectRatio !== this.props.aspectRatio) {
this.setAspectRatio(nextProps.aspectRatio);
}
if (nextProps.data !== this.props.data) {
this.setData(nextProps.data);
}
if (nextProps.dragMode !== this.props.dragMode) {
this.setDragMode(nextProps.dragMode);
}
if (nextProps.cropBoxData !== this.props.cropBoxData) {
this.setCropBoxData(nextProps.cropBoxData);
}
if (nextProps.canvasData !== this.props.canvasData) {
this.setCanvasData(nextProps.canvasData);
}
if (nextProps.moveTo !== this.props.moveTo) {
if (nextProps.moveTo.length > 1) {
this.moveTo(nextProps.moveTo[0], nextProps.moveTo[1]);
} else {
this.moveTo(nextProps.moveTo[0]);
}
}
if (nextProps.zoomTo !== this.props.zoomTo) {
this.zoomTo(nextProps.zoomTo);
}
if (nextProps.rotateTo !== this.props.rotateTo) {
this.rotateTo(nextProps.rotateTo);
}
if (nextProps.scaleX !== this.props.scaleX) {
this.scaleX(nextProps.scaleX);
}
if (nextProps.scaleY !== this.props.scaleY) {
this.scaleY(nextProps.scaleY);
}
if (nextProps.enable !== this.props.enable) {
if (nextProps.enable) {
this.enable();
} else {
this.disable();
}
}
Object.keys(nextProps).forEach((propKey) => {
let isDifferentVal = nextProps[propKey] !== this.props[propKey];
const isUnchangeableProps = unchangeableProps.indexOf(propKey) !== -1;
if (typeof nextProps[propKey] === 'function' && typeof this.props[propKey] === 'function') {
isDifferentVal = nextProps[propKey].toString() !== this.props[propKey].toString();
}
if (isDifferentVal && isUnchangeableProps) {
throw new Error(`prop: ${propKey} can't be change after componentDidMount`);
}
});
}
componentWillUnmount() {
if (this.img) {
// Destroy the cropper, this makes sure events such as resize are cleaned up and do not leak
this.cropper.destroy();
delete this.img;
delete this.cropper;
}
}
setDragMode(mode) {
return this.cropper.setDragMode(mode);
}
setAspectRatio(aspectRatio) {
return this.cropper.setAspectRatio(aspectRatio);
}
getCroppedCanvas(options) {
return this.cropper.getCroppedCanvas(options);
}
setCropBoxData(data) {
return this.cropper.setCropBoxData(data);
}
getCropBoxData() {
return this.cropper.getCropBoxData();
}
setCanvasData(data) {
return this.cropper.setCanvasData(data);
}
getCanvasData() {
return this.cropper.getCanvasData();
}
getImageData() {
return this.cropper.getImageData();
}
getContainerData() {
return this.cropper.getContainerData();
}
setData(data) {
return this.cropper.setData(data);
}
getData(rounded) {
return this.cropper.getData(rounded);
}
crop() {
return this.cropper.crop();
}
move(offsetX, offsetY) {
return this.cropper.move(offsetX, offsetY);
}
moveTo(x, y) {
return this.cropper.moveTo(x, y);
}
zoom(ratio) {
return this.cropper.zoom(ratio);
}
zoomTo(ratio) {
return this.cropper.zoomTo(ratio);
}
rotate(degree) {
return this.cropper.rotate(degree);
}
rotateTo(degree) {
return this.cropper.rotateTo(degree);
}
enable() {
return this.cropper.enable();
}
disable() {
return this.cropper.disable();
}
reset() {
return this.cropper.reset();
}
clear() {
return this.cropper.clear();
}
replace(url, onlyColorChanged) {
return this.cropper.replace(url, onlyColorChanged);
}
scale(scaleX, scaleY) {
return this.cropper.scale(scaleX, scaleY);
}
scaleX(scaleX) {
return this.cropper.scaleX(scaleX);
}
scaleY(scaleY) {
return this.cropper.scaleY(scaleY);
}
render() {
const {
src,
alt,
crossOrigin,
style,
className,
} = this.props;
return (
<div
style={style}
className={className}
>
<img
crossOrigin={crossOrigin}
ref={(img) => { this.img = img; }}
src={src}
alt={alt === undefined ? 'picture' : alt}
style={{ opacity: 0 }}
/>
</div>
);
}
}
ReactCropper.propTypes = {
style: PropTypes.object, // eslint-disable-line react/forbid-prop-types
className: PropTypes.string,
// react cropper options
crossOrigin: PropTypes.string,
src: PropTypes.string,
alt: PropTypes.string,
// props of option can be changed after componentDidmount
aspectRatio: PropTypes.number,
dragMode: PropTypes.oneOf(['crop', 'move', 'none']),
data: PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number,
width: PropTypes.number,
height: PropTypes.number,
rotate: PropTypes.number,
scaleX: PropTypes.number,
scaleY: PropTypes.number,
}),
scaleX: PropTypes.number,
scaleY: PropTypes.number,
enable: PropTypes.bool,
cropBoxData: PropTypes.shape({
left: PropTypes.number,
top: PropTypes.number,
width: PropTypes.number,
height: PropTypes.number,
}),
canvasData: PropTypes.shape({
left: PropTypes.number,
top: PropTypes.number,
width: PropTypes.number,
height: PropTypes.number,
}),
zoomTo: PropTypes.number,
moveTo: PropTypes.arrayOf(PropTypes.number),
rotateTo: PropTypes.number,
// cropperjs options
// https://github.com/fengyuanchen/cropperjs#options
// aspectRatio, dragMode, data
viewMode: PropTypes.oneOf([0, 1, 2, 3]),
preview: PropTypes.string,
responsive: PropTypes.bool,
restore: PropTypes.bool,
checkCrossOrigin: PropTypes.bool,
checkOrientation: PropTypes.bool,
modal: PropTypes.bool,
guides: PropTypes.bool,
center: PropTypes.bool,
highlight: PropTypes.bool,
background: PropTypes.bool,
autoCrop: PropTypes.bool,
autoCropArea: PropTypes.number,
movable: PropTypes.bool,
rotatable: PropTypes.bool,
scalable: PropTypes.bool,
zoomable: PropTypes.bool,
zoomOnTouch: PropTypes.bool,
zoomOnWheel: PropTypes.bool,
wheelZoomRatio: PropTypes.number,
cropBoxMovable: PropTypes.bool,
cropBoxResizable: PropTypes.bool,
toggleDragModeOnDblclick: PropTypes.bool,
minContainerWidth: PropTypes.number,
minContainerHeight: PropTypes.number,
minCanvasWidth: PropTypes.number,
minCanvasHeight: PropTypes.number,
minCropBoxWidth: PropTypes.number,
minCropBoxHeight: PropTypes.number,
ready: PropTypes.func,
cropstart: PropTypes.func,
cropmove: PropTypes.func,
cropend: PropTypes.func,
crop: PropTypes.func,
zoom: PropTypes.func,
};
ReactCropper.defaultProps = {
src: null,
dragMode: 'crop',
data: null,
scaleX: 1,
scaleY: 1,
enable: true,
zoomTo: 1,
rotateTo: 0,
};
export default ReactCropper;
2.cropper组件调用的简单封装
CropperView.jsx文件
import React, { Component, useEffect } from 'react';
import $ from "jquery";
import Cropper from './cropper/react-cropper'
import 'cropperjs/dist/cropper.css'
/* global FileReader */
var showCropArea = true;
export default class CropperView extends Component {
constructor(props) {
super(props);
this.state = {
cropResult: null,
};
// this.cropper = this;
this.onChange = this.onChange.bind(this);
this.src = props.src;
}
// componentDidMount(){
// useEffect(() => {
// alert("cropZone" + this.cropper);
// // if (typeof this.cropper.getCroppedCanvas() === 'undefined') {
// // return;
// // }
// alert("left:" + this.cropper.getCropBoxData().left
// + "top:" + this.cropper.getCropBoxData().top
// + "width:" + this.cropper.getCropBoxData().width
// + "height:" + this.cropper.getCropBoxData().height);
// }, [this.props.save]);
// }
onChange(e) {
e.preventDefault();
let files;
if (e.dataTransfer) {
files = e.dataTransfer.files;
} else if (e.target) {
files = e.target.files;
}
const reader = new FileReader();
reader.onload = () => {
this.setState({ src: reader.result });
};
reader.readAsDataURL(files[0]);
}
cropZone() {
if (typeof this.cropper.getCroppedCanvas() === 'undefined') {
return;
}
alert("left:" + this.cropper.getCropBoxData().left
+ "top:" + this.cropper.getCropBoxData().top
+ "width:" + this.cropper.getCropBoxData().width
+ "height:" + this.cropper.getCropBoxData().height);
$(".cropper-crop-box").hide();
$(".cropper-drag-box").hide();
$(".cropper-wrap-box").append(
"<div style=\"width:" + this.cropper.getCropBoxData().width + ";"
+ "height:" + this.cropper.getCropBoxData().height + ";"
+ "background:#0000FF; opacity:0.3"
+ "position:absolute; left:" + this.cropper.getCropBoxData().left +";"
+ "top:" + this.cropper.getCropBoxData().top + ";\">");
}
showCropZone() {
if (showCropArea) {
$(".cropper-crop-box").css("display", "block");
$(".cropper-drag-box").css("display", "block");
} else {
$(".cropper-crop-box").hide();
$(".cropper-drag-box").hide();
}
}
creatCrop(){
this.cropper.crop();
}
clearCrop(){
this.cropper.clear();
}
resetCrop(){
this.cropper.reset();
}
moveLeft(){
this.cropper.move(-5, 0);
}
moveRight(){
this.cropper.move(5, 0);
}
moveUp(){
console.log("====moveUp===");
try {
this.cropper.move(0, -5);
}catch (err){
console.log(err);
}
}
moveDown(){
this.cropper.move(0, 5);
}
enlarge(){
try {
// 放大
// this.cropper.zoom(0.1);
var allCanvasDate = this.cropper.getCanvasData();
var newCanvasDate = {left:allCanvasDate.left, top: allCanvasDate.top,
width: allCanvasDate.width*2, height: allCanvasDate.height*2}
this.cropper.setCanvasData(newCanvasDate);
}catch (err){
console.log(err);
}
}
shrink(){
try {
// 缩小
// this.cropper.zoom(-0.1)
var allCanvasDate = this.cropper.getCanvasData();
var newCanvasDate = {left:allCanvasDate.left, top: allCanvasDate.top,
width: allCanvasDate.width*0.5, height: allCanvasDate.height*0.5}
this.cropper.setCanvasData(newCanvasDate);
}catch (err){
console.log(err);
}
}
test(){
try {
var allDate = this.cropper.getData(true);
alert(allDate.toString());
var par = {x:allDate.x, y:allDate.y, width:allDate.width*2, height:allDate.height*2,
rotate:allDate.rotate, scaleX:allDate.scaleX, scaleY:allDate.scaleY}
this.cropper.setData(par);
}catch (err){
console.log(err);
}
}
moveCrop(){
//this.cropper.movecrop();
console.log("===move===crop===");
}
reduceCrop(){
var par = {left:this.cropper.getCropBoxData().left, top:this.cropper.getCropBoxData().top,
width:this.cropper.getCropBoxData().width*0.8, height:this.cropper.getCropBoxData().height*0.8}
this.cropper.setCropBoxData(par);
}
raiseCrop(){
var par = {left:this.cropper.getCropBoxData().left, top:this.cropper.getCropBoxData().top,
width:this.cropper.getCropBoxData().width*1.2, height:this.cropper.getCropBoxData().height*1.2}
this.cropper.setCropBoxData(par);
}
CropLeft(){
var par = {left:this.cropper.getCropBoxData().left - 10, top:this.cropper.getCropBoxData().top,
width:this.cropper.getCropBoxData().width, height:this.cropper.getCropBoxData().height}
this.cropper.setCropBoxData(par);
}
CropRight(){
var par = {left:this.cropper.getCropBoxData().left + 10, top:this.cropper.getCropBoxData().top,
width:this.cropper.getCropBoxData().width, height:this.cropper.getCropBoxData().height}
this.cropper.setCropBoxData(par);
}
CropUp(){
var par = {left:this.cropper.getCropBoxData().left, top:this.cropper.getCropBoxData().top - 10,
width:this.cropper.getCropBoxData().width, height:this.cropper.getCropBoxData().height}
this.cropper.setCropBoxData(par);
}
CropDown(){
var par = {left:this.cropper.getCropBoxData().left, top:this.cropper.getCropBoxData().top + 10,
width:this.cropper.getCropBoxData().width, height:this.cropper.getCropBoxData().height}
this.cropper.setCropBoxData(par);
}
render() {
return (
<div className="r-view" style={{ position:'absolute', left:'185px', top:"83px" }}>
<Cropper
style={{ height:'100%', width:'auto' }}
aspectRatio={16 / 9}
preview=".img-preview"
guides={false}
src={this.props.src}
viewMode={2}
minContainerWidth={585}
minContainerHeight={430}
ref={cropper => { this.cropper = cropper; }}
zoomable={true}
zoomOnTouch={true}
/>
</div>
);
}
}
3.cropper组件与各种按钮操作的绑定(原因:设备上不会支持手指在选择区域的操作以及图片的放大缩小操作),页面整合组件。
CropperScreen.tsx文件
import * as React from 'react';
import styled from "styled-components";
import useTranslate from "../../hooks/useTranslate";
import CropView from "./CropperView";
import 'cropperjs/dist/cropper.css' import FormView from "./FormView" const AppStyle = styled.div`
background: #CCC;
`; var showCropArea = false; export default function CropperScreen() {
const t = useTranslate(); const handleBackClicked = () => {
$("#viewable").show();
$("#scan_settings_id").css("display","none");
}; const doCropClicked = () => {
// showCropArea = !showCropArea;
// if (showCropArea) {
// alert("disabled false");
// $("#btn_save_crop").removeAttr("disabled");
// } else {
// alert("disabled true");
// $("#btn_save_crop").attr("disabled", "true");
// }
// cropUser.showCropZone(showCropArea); cropUser.clearCrop(); }; const doSaveCropClicked = () => {
cropUser.cropZone();
}; const handReset = () => {
cropUser.resetCrop();
cropUser.creatCrop();
} const handMoveLeft = () => {
cropUser.moveLeft();
} const handMoveRight = () => {
cropUser.moveRight();
} const handMoveUp = () => {
console.log("====handMoveUp===");
try {
cropUser.moveUp();
}catch (err){
console.log(err);
}
} const handMoveDown = () => {
cropUser.moveDown();
} const handMoveCrop = () => {
alert("unknow");
cropUser.moveCrop();
} const handReduceCrop = () => {
cropUser.reduceCrop();
} const handRaiseCrop = () => {
cropUser.raiseCrop()
} const handEnlarge = () => {
cropUser.enlarge();
} const handShrink = () => {
cropUser.shrink();
} const handCropLeft = () => {
cropUser.CropLeft()
} const handCropRight = () => {
cropUser.CropRight()
} const handCropUp = () => {
cropUser.CropUp()
} const handCropDown = () => {
cropUser.CropDown()
} const handTest = () => {
cropUser.test()
} const handleSubmit = () => {
alert("====handleSubmit====");
console.log(formUser); console.log(formUser.state);
} let maxItem = 2;
let cropUser
let formUser
var initValue = {name:'tom',job: '12'} return (
<AppStyle id="scan_settings_id" className="r-view" style={{display:"none", height:"470px"}}>
<header className="r-titlebar">
<a href="#" className="r-titlebar__back" onClick={handleBackClicked} />
<h1 className="r-titlebar__title">Preview</h1>
</header>
<div style={{position:"absolute", left:35}}>
</div>
<CropView ref={cropView => {cropUser = cropView}} src={require('../smartsdk-css/img/test.png')}/>
<div className="r-floating-island">
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={doCropClicked}>Crop</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={doSaveCropClicked}>Save</button>
</div>
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handReset}>reset</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveCrop}>mcrop</button>
</div>
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveLeft}>left</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveRight}>right</button>
</div>
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveUp}>up</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={handMoveDown}>down</button>
</div>
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handEnlarge}>+</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handShrink}>-</button>
</div>
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handReduceCrop}>reduce</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handRaiseCrop}>raise</button>
</div> <div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handCropLeft}>cropr</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handCropRight}>cropl</button>
</div> <div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handCropUp}>cropu</button>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handCropDown}>cropd</button>
</div>
<div>
<button style={{width:'70px', height:'35px', marginLeft:'5px'}} onClick={ handTest}>test</button>
</div> {/* <FormView handleSubmit={handleSubmit} ref={formView => {formUser = formView}} props = {initValue}/> */} {/* <button className="r-start-button">
{t("dapi:cba.common.start")}
</button> */}
</div>
</AppStyle >
);
}
4.最后调用整体的组件,页面展示

页面

react中使用截图组件Cropper组件的更多相关文章
- React中的Context——从父组件传递数据
简介:在React中,数据可以以流的形式自上而下的传递,每当你使用一个组件的时候,你可以看到组件的props属性会自上而下的传递.但是,在某些情况下,我们不想通过父组件的props属性一级一级的往下传 ...
- react 中的无状态函数式组件
无状态函数式组件,顾名思义,无状态,也就是你无法使用State,也无法使用组件的生命周期方法,这就决定了函数组件都是展示性组件,接收Props,渲染DOM,而不关注其他逻辑. 其实无状态函数式组件也是 ...
- 基于 React 实现一个 Transition 过渡动画组件
过渡动画使 UI 更富有表现力并且易于使用.如何使用 React 快速的实现一个 Transition 过渡动画组件? 基本实现 实现一个基础的 CSS 过渡动画组件,通过切换 CSS 样式实现简单的 ...
- 理解React中es6方法创建组件的this
首发于:https://mingjiezhang.github.io/(转载请说明此出处). 在JavaScript中,this对象是运行时基于函数的执行环境(也就是上下文)绑定的. 从react中的 ...
- React中父组件与子组件之间的数据传递和标准化的思考
React中父组件与子组件之间的数据传递的的实现大家都可以轻易做到,但对比很多人的实现方法,总是会有或多或少的差异.在一个团队中,这种实现的差异体现了每个人各自的理解的不同,但是反过来思考,一个团队用 ...
- React 深入系列1:React 中的元素、组件、实例和节点
文:徐超,<React进阶之路>作者 授权发布,转载请注明作者及出处 React 深入系列,深入讲解了React中的重点概念.特性和模式等,旨在帮助大家加深对React的理解,以及在项目中 ...
- [转] React 中组件间通信的几种方式
在使用 React 的过程中,不可避免的需要组件间进行消息传递(通信),组件间通信大体有下面几种情况: 父组件向子组件通信 子组件向父组件通信 跨级组件之间通信 非嵌套组件间通信 下面依次说下这几种通 ...
- react中直接调用子组件的方法(非props方式)
我们都知道在 react中,若要在父组件调用子组件的方法,通常我们会采用在父组件定义一个方法,作为props转给子组件,然后执行该方法,可以获取到子组件传回的参数以得到我们的目的. 显而易见,这个执行 ...
- React 中的 Component、PureComponent、无状态组件 之间的比较
React 中的 Component.PureComponent.无状态组件之间的比较 table th:first-of-type { width: 150px; } 组件类型 说明 React.c ...
随机推荐
- CSRF跨站请求伪造漏洞分析
CSRF 现在的网站都有利用CSRF令牌来防止CSRF,就是在请求包的字段加一个csrf的值,防止csrf,要想利用该漏洞,要和xss组合起来,利用xss获得该csrf值,在构造的请求中将csrf值加 ...
- Solution -「LOCAL」「cov. 牛客多校 2020 第五场 C」Easy
\(\mathcal{Description}\) Link.(完全一致) 给定 \(n,m,k\),对于两个长度为 \(k\) 的满足 \(\left(\sum_{i=0}^ka_i=n\r ...
- Dubbo基础三之配置方式简述
Dubbo基础一之实战初体验 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中,体验了两种配置方式一种注解一种xml.其中xml是在注解配置失败没有找到解决方法后选择xml替代体验的.那 ...
- ACM对抗赛有感
2022.2.22 一个有"爱"的日子,注定不会平凡(对于24oier来说),原因是gg让我们参加与大连理工大学的对抗赛. 为此队友都准备好各种板子,上了比赛才发现根本没有 可怜了 ...
- 信而泰IPv6协议一致性测试解决方案
信而泰IPv6协议一致性测试解决方案 背景 中国已经开始逐步进入万物互联的社会,相比原来的手机.电脑等接入网络,万物互联时代接入网络的智能终端会海量增加,而且在万物互联时代,网络的流量巨大,互联的 ...
- 思迈特软件 Smartbi数据查询能力如何?
随着对BI应用程度的加深,用户需要连接和管理的数据越来越多,也越来越复杂. Smartbi支持丰富的数据源接入,但一般并不能直接使用接入的业务库直接进行数据分析.所以在报表开发前的取数过程,把需要的数 ...
- Smartbi报表产品靠易用性出圈,国内口碑第一的BI厂商
有调查显示,在对用户最关注商业智能哪些方面的调查中发现超过19%的被调查者认为产品易用性非常重要.在商业智能继续火热的2021年,BI产品的易用性深受用户的关注,并成为了选择产品的第一考虑要素. 在注 ...
- ASP.NET Core 6框架揭秘实例演示[15]:针对控制台的日志输出
针对控制台的ILogger实现类型为ConsoleLogger,对应的ILoggerProvider实现类型为ConsoleLoggerProvider,这两个类型都定义在 NuGet包"M ...
- Qt:打印输出到控制台,类似C++的cout
1. #include<qDebug> 2. qDebug<<"Hello,world!"; 补充,如果不是控制台文件,比如是窗口应用程序,需要在pro文件 ...
- c/c++ 内存泄漏分析
Valgrind: https://zhuanlan.zhihu.com/p/111556601 valgrind输出结果分析 valgrind输出结果会报告5种内存泄露,"definite ...